Sledujte nás na YouTube

Handwrite Notes: Jak aplikace vznikala a co bylo potřeba řešit?

Aplikace Handwrite Notes umožňuje rychlé zapisování „kreslených“ poznámek. Při její tvorbě bylo potřeba řešit zajímavé technické otázky, které vám přiblíží právě tento článek.

Do Windows Phone Marketplace mi v minulých dnech přibyla nová aplikace s názvem Handwrite Notes. Ta umožňuje rychlé zapisování poznámek na telefonu. Text se nezadává standardně z klávesnice, ale pro jeho vkládání slouží panel ve spodní části displeje. Tahem prstu po této plošce si může uživatel vkládat slova nebo nákresky, jako by je psal na papír. Text se mu postupně umisťuje zmenšený na horní řádky. Tuto aplikaci může využít pro rychlé poznamenání si nákupního seznamu, vložení matematického výrazu, nebo pro zapsání textu v obecně telefonem nepodporované abecedě (psací písmo, čínské nebo japonské znaky, těsnopis…).

  

 

Jednotlivé zápisky si může uživatel připnout na hlavní obrazovku jako živé dlaždice a mít je tak pořád na očích. Aplikace podporuje zobrazení na výšku i na šířku, stejně tak i světlé a tmavé barevné téma. Zápisky jdou po přihlášení synchronizovat se SkyDrive účtem, uživatel si je také může uložit jako obrázek do galerie a potom například odeslat e-mailem. I když aplikace vypadá poměrně jednoduše, při její tvorbě bylo potřeba řešit mnoho zajímavých technických otázek. Chtěl bych se k tomu tady ještě vrátit. Věřím, že to bude zajímavé čtení i pro lidi, co se přímo programováním nezabývají.

-

Proč zápisky?

Proč jsem se vlastně rozhodl takovou aplikaci vytvořit? Na původních Windows Mobile ruční zadávání textu poměrně dobře fungovalo a znal jsem i hodně lidí, co ho používali. Pro Windows Phone 7 jsem zatím podobnou aplikaci nenašel. Jediné dostupné programy měly problém s rychlostí, rychle kreslené čáry byly zubaté, při jejich větším počtu byla aplikace zpomalená. Žádný program nenabízel možnost kreslení po přiblížené plošce, ani například vložení tečky nad písmenem jednoduchým kliknutím. Přitom například na iOS existuje takových aplikací několik. Proto jsem se rozhodl vytvořit prototyp nového programu. Ten jsem na svém Twitteru nabídl lidem k otestování, ohlasy byly dobré.

Zadávání slov

Asi první zajímavá věc na této aplikaci je použitá technologie. Rozhodl jsem se ji celou napsat v herním XNA Frameworku, ne ve standardním Silverlightu. Chtěl jsem dosáhnout co nejrychlejšího načítání i zpracovávání zadávání písma. Pro snímání vstupu jsem využil běžnou herní smyčku, rozeběhnutou na 60 snímků za sekundu. Podle dotyků na zadávací plošce jsem potom stavěl samotnou geometrii slov.

  

Veškeré čáry jsou skládány z malých trojúhelníčků (vertexů a indexů), které jsou potom přímo posílány na grafickou kartu. Je to tedy stejný způsob, jako se používá při generování nových objektů přímo za běhu v některých 3D hrách. Podle rychlosti tažení prstu se určuje i barva a tloušťka dané části čáry. Geometrie je skládána tak, aby jí ani rychlé změny směru tahu nevadily, vždy byla zachována správná šířka čáry a pokud možno tam nevznikaly žádné artefakty. Zajímavé pozorování bylo, že většina složitějších optimalizací zde přinášela spíš problémy plynoucí z nepřesností při zakrouhlování čísel.

Po dokončení zápisu se slova umisťují na plán, kde se ještě dále optimalizují. Zjednoduší se jejich geometrie (počet trojúhelníků), aby se ušetřila paměť a snížil počet objektů posílaných na grafickou kartu. Přibližně rovné čáry se díky tomu opticky vyhladí a o něco vyrovnají. Slova na plánu jsou ukládána do seznamu seznamů, podle jejich pozic jsou vždy vykreslována jen ta, která jsou aktuálně vidět (se samotným plánem jde totiž posouvat).

Testování výkonu

Bylo zajímavé testovat limity těchto telefonů. Na Samsungu Omnia 7 (zařízení první generace) nebyl problém vykreslit až 15 tisíc trojúhelníčků na jedno volání Draw, aplikace běžela stále okolo 55 snímků za sekundu. Při větším počtu vertexů už ale výkon poměrně dost rychle klesal. Doplňoval jsem ještě určitou optimalizaci – v případě velké zátěže se sníží obnovovací frekvence horního plánu, aby spodní zadávací ploška mohla zpracovávat data rychleji.

Zkoušel jsem aplikovat i antialiasing (vyhlazování čar), to už bylo ale výkonově hodně na hraně, od toho jsem nakonec upustil. Poslední zajímavá vlastnost, kterou jsem implementoval, bylo “zahazování” snímků k vykreslení. Pokud se na plánu nic nezměnilo, přeskočilo se daný snímek volání metody Draw. Docela to mohlo pomoci ušetřit baterii telefonu.

Další funkce

Když už jsem měl vyladěné vykreslovací jádro, začal jsem doplňovat zbylé funkce. Přidal jsem podporu gest – tažení prstu zprava doleva aby smazalo poslední slovo, tažení shora dolů udělalo odřádkování. Ač se to nezdálo, tohle bylo také docela náročné odladit. Při změně orientace displeje ze šířky na výšku totiž slova mohla přetékat na více řádků. První vkládané slovo na řádek také dostávalo určitou “kotvu”, aby se ho přetékání netýkalo. Počítal jsem už i s tím, že by v další verzi aplikace mohly jít měnit velikosti řádků. Potřeboval jsem tedy ohlídat, aby se vždy smazalo správné slovo, i když se například nacházelo úplně na konci řádku a kreslené gesto už přesahovalo dál.

Na zadávací plošku jsem také doplňoval podporu rozpoznání psaní nového slova. Pokud uživatel při psaní přesáhne určitou hranici, může začít psát rovnou nové slovo znovu od začátku a nemusí čekat, až se předchozí slovo vloží na plán. Toto jsou ale většinou věci, které běžný člověk nemusí řešit, fungují pro něj automaticky a ani si jich nevšimne.

  

Dále jsem do aplikace doplňoval spodní výsuvný panel a ostatní obrazovky (s výběrem dlaždic apod.). Na tohle XNA už nebylo úplně stavěné, ale nějak jsem to zvládl. Na správu a přepínání obrazovek jsem využil část svého 3D engine. Pro doplnění dalších funkcí se mi hodně hodilo nastudovat volání systémových Windows Phone funkcí, jako jak například uložit obrázek z aplikace, jak nastavit sekundární dlaždici apod. Na žádný velký problém jsem tam ale nenarazil.

Pro zajímavost, v prvních verzích aplikace bylo nutné posouvat text na plánu dvěma prsty, jedním prstem se přesouval kurzor. Nebylo to ale úplně ergonomické, ne každého to také napadlo používat. Ovládání jsem potom raději upravil na to standardní, co se vyskytuje i jinde v systému, včetně malého dojezdu po puštění prstu.

Ukládání dat

Chtěl jsem, aby se aplikace používala co nejjednodušeji a co nejrychleji. Určitě tedy bylo důležité, aby si sama ukládala svoje data, nemusela tím uživatele nijak zatěžovat. Snažil jsem se naimplementovat korektní podporu tombstoningu. Dával jsem pozor i na to, když si například uživatel připne z aplikace sekundární dlaždici a rovnou spustí aplikaci znovu, aby o svoje data nepřišel. Snad se mi to povedlo odladit všechno. Bylo důležité, aby si vždy aplikace uložila všechny svoje údaje a zaktualizovala potřebné dlaždice do 5 sekund od vypnutí. Poté by ji mohl zabít operační systém.

V první verzi ukládání jsem narazil docela na problém. Zaprvé jsem zjistil, že na Windows Phone nelze použít standardní binární serializaci objektů, je možné je ukládat jen do XML. Zadruhé, i když jsem zkusil využít docela povedenou knihovnu sharpSerializer, pořád to bylo moc pomalé. Datovou strukturu s plánem jsem měl moc složitou, vertexů i indexů bylo hodně, trvalo to moc dlouho.

Musel jsem si velice rozmyslet, co všechno budu ukládat. Zaprvé jsem naimplementoval to zjednodušování geometrie už umístěných slov na plánu. Potom jsem také renderování upravil tak, aby indexy vertexů šly vždy ve stejném pořadí za sebou. Pouze jsem si ukládal, kde začíná a končí jaká čára. Také jsem upravil další položky, barvu každého bodu jsem shrnul jen do jednoho byte apod. Naštěstí jsem tím dosáhl poměrně výrazného zrychlení, což mi rozhodně udělalo radost :)

Synchronizace se SkyDrive

Když už jsem měl aplikaci hotovou, říkal jsem si, že by tam bylo dobré doplnit ještě určitou možnost synchronizace s internetem. Rozhodl jsem se naimplementovat podporu SkyDrive pomocí oficiálního LIVE SDK. Jako velký problém se ukázalo, že přihlašovací obrazovku pro získání přístupových práv nejde vyvolat z čistého XNA. Pomocí WebBrowserTask jde sice webovou stránku zobrazit, už tímto způsobem ale nejdou získat zpět potřebná data. Stejný problém bychom měli například s integrací Facebooku do nějaké hry. Jediné řešení tedy bylo oklopit aplikaci do hybridního projektu, kde lze kombinovat Silverlight i XNA dohromady.

Nebylo to nic příjemného, ta portace mi zabrala hodiny. V Silverlight/XNA projektu je hodně věcí jinak. Nemáme k dispozici objekt Game, ze kterého bychom si mohli vytahovat objekt Viewport, ani komponenty typu DrawableGameComponent. Jiné je předávání objektu GameTime, každé volání GraphicsDeviceManager musíme nahradit za SharedGraphicsDeviceManager. V Silverlightu se také odlišně řeší orientace displeje i správa obrazovek (k tomuto tématu se ještě vrátíme v posledních videích ze seminářů). Na většinu volání jsem ale naštěstí našel alternativy. Ve výsledku nakonec ani nebylo o tolik větší zatížení paměti, ani se nijak výrazně nezpomalil start aplikace.

Do finální verze padlo pouze několik drobností. V Silverlight/XNA projektu se nedá kreslit s antialiasingem do textury, také nejspíš nejdou úplně jednoduše vyhazovat snímky k vykreslení, pokud se na plánu nic nezměnilo. Je k dispozici pouze metoda SupressFrame, která se chová trochu jinak, než přepsané volání BeginDraw v čistém XNA (nejspíš se zahazuje nejen volání Draw, ale i Update).

Také jsem se nakonec musel vzdát zobrazení horní lišty s hodinami v portrait módu, nepodařilo se mi vyladit vykreslování té 3D části, aby nebylo zkreslené. Navíc, ne-fullscreen mód v Silverlight/XNA přidával po stranách okna ještě další zbytečné okraje, na rozdíl od čistého XNA. Tohle byly ale naštěstí jen kosmetické drobnosti, za tu možnost synchronizace se SkyDrive to určitě stálo.

Samotná integrace SkyDrive byla spíš jen zdlouhavá, asynchronní kód bez klíčových slov async a await nabobtnal na hodně řádků kódu. Chtělo si tam pohlídat korektní zavírání všech streamů do Isolated Storage, také si ověřovat, jak různě může kód probíhat. Když například složka na SkyDrive už byla vytvořena, metoda mohla skončit a další části synchronizace se nemusely zavolat. API bylo dobře připravené, včetně několika vzových samplů. Zaujalo mě tam ale to, že si člověk nemůže jednoduše vyžádat konkrétní podsložku zavoláním celé její cesty, vždy musí jít postupně přes nadřazené položky, získat od nich správná ID, znovu se dotázat dál… A to všechno opět asynchronně. Už chápu, proč si většina autorů raději ušetří práci a soubory z mobilu na SkyDrive nakopíruje přímo do hlavní kořenové složky.

Budoucnost aplikace

Do budoucna bych chtěl do aplikace doplnit ještě několik drobností. Zajímavá by byla podpora dalších jazyků v rámci lokalizace, určitě němčiny a francouzštiny, ale například i ruštiny nebo čínštiny. V určitých zemích by mohla mít úspěch také podpora psaní zprava doleva :) O rozpoznávání písma na text zatím neuvažuji, takovou knihovnu, která by dobře rozpoznávala i české psací písmo, nebo čínské znaky, jsem zatím nenašel. Chápu, že tohle bude asi nejčastější otázka, snad ale budou lidé chápat, že zaměření téhle aplikace je trochu jiné. Ostatní funkce ještě uvidím, podle ohlasů lidí.

Určitě se nebojte aplikaci odzkoušet a dát mi vědět, budu se na vaši zpětnou vazbu těšit. U aplikace je dostupný i trial, který poskytuje téměř všechny funkce, kromě exportu na SkyDrive a možnosti připínání více dlaždic. Diskuzi k této aplikaci najdete i na našem fóru. Detail si můžete prohlédnout v katalogu, kde najdete i všechny mé další aplikace (Vroomstar a Galaxy Jet).

Tomáš Slavíček

7 komentářů

  1. mimonaut (neregistrovaný)

    Hm.. zajímavý čtení a to i přes to, že polovině věcí nerozumim. Každopádně je pro mě překvapující, kolik člověk u takovéhle zdánlivě jednoduché aplikace musí řešit problémů, aby to odpovídalo jeho požadavkům. A taky mě zaujala použitá technologie.

  2. Petrroll (neregistrovaný)

    Existuje šance, že by se někdy objevily zdrojáky aplikace / nějaké její části?

    • Tomáš Slavíček (neregistrovaný)

      Veřejné uvolnění neplánuji.

    • Petrroll (neregistrovaný)

      Ani částečné, třeba ošetřené šílenou licencí zakazující třeba i čtení ve více lidech najednou? ;)

      Případně jen pro smartmania komunitu?

    • Tomáš Slavíček (neregistrovaný)

      Poslal jsem ti soukromou zprávu… Je tam docela dost know-how, také se už musím začít chovat trochu komerčně. Programátorům rád pomůžu, určitě zase připravím další články apod., ale nemůžu dávat vše úplně všem :)

  3. Tomáš Herceg (neregistrovaný)

    XNA na telefonech není v metodě DrawPrimitives možnost kreslit LineList? Tím by se daly kreslit jen čáry a nebylo by potřeba řešit to přes trojúhelníky – i když pokud by čára měla být tlustší, tak by to moc nepomohlo.

    Máš případně nějaké měření, jak rychlé je toto 3D řešení ve srovnání s kreslením do textury (samozřejmě data bys uchovával vektorově, ale při změně by se jen vše vyrenderovalo do textury a ta se pak zobrazovala).

    • Tomáš Slavíček (neregistrovaný)

      Díky za zajímavou otázku. V XNA na WP7 je možnost kreslit LineList i LineStrip… Tím ale lze podle mě dosáhnout jen 1px široké čáry.

      Ohledně renderování do textury, v XNA 4.0 Texture2D a RenderTarget2D prakticky splývají. Jen se přesměruje grafický device, kam se má kreslit. Pokud to chápu správně, vykreslování by mělo být stejně rychlé. Pouze kdybychom pak chtěli texturu uložit, to už by něco trvalo (data by se musela zkopírovat z grafické paměti).

Napsat komentář

Vaše emailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *