Apple、22日にスペシャルイベント開催、「話すことがたくさんある」と予告 | マイナビニュースの前に、Apple TV のウワサに水をかける:MG Siegler | maclalala2と信じられないことが起きた日 | maclalala2ネタ。
Apple TVについてはどうだろう。何か機能や番組の追加があればうれしいけどね。期待せずに待ちますかね。
「信じられないことが起きた日」はiTunes for Windowsをジョブズの反対を押し切って出したというエピソード。これは正解だった。僕がAppleユーザーになる切っ掛けを作ったのだから。
AppleはMacをもっと売りたいはずだが、思いがけなくiPadが売れて、その目算が崩れた。PC/Macだけの時代は終焉し、iPadを中心とするタブレットの時代が訪れた。しかし、マイクロソフトはWindows 8.1タブレットで反攻を開始し、Googleは自身のAndroidのハードウェアで成功できず、Chromebookに重心を移しはじめた。モバイルとデスクトップの両方を支配するための戦いが始まったのだ。
この戦いは微妙である。Windows 8.1はWebからダウンロードすると自動的にSkyDriveに保存される。モバイルではデバイスのメモリが制限されるため、クラウドは必要だが、デスクトップではいらぬおせっかいだということもできる。当然モバイルでは通信環境も必要になる。デスクトップを持ち運ぶことができるメリットは大きいが、Windows 8.1タブレットは通信環境をどう確保するのだろう。Windows Phoneとの連携はどうなっているのか。
Googleは今のところクラウド前提のコンピューティングに徹している。Chromecastも出してきたし、ホームユースはWindows/Mac上のChromeでいくのか、Chromebookが補完するのか、もう少し見えてこない。Google Glassとの関係は・・・
AppleはiTunesとApple TVをトロイの木馬として、iPhone/iPadがキーデバイスとなる。ユーザーはPC/Macを好むユーザーとは限らない。iPadが64bitマシンとなってどう進化するのか。Macに近づくのか。話題のiWatchは・・・
問題に焦点を合わせにくくなっている。オフィスアプリから考えると、WindowsはOfficeそのもの、GoogleはGoogle Apps、AppleはiWork。Windows/MacにはLibre Officeもある。Webブラウジングはどれも同じ。
ミュージック・ストアはiTunes Storeが有利だろう。iTunesやApple TVによるメディア再生と一貫性がある。iCloudによる共有は強力な訴求点だ。ここの競争相手はむしろAmazonだね。
ブック・ストアはバラバラ。あっちで買ったり、こっちで買ったりと分けがわからなくなっている。iOSのアプリとしての本もあるし、iBooksもある。その他、Kindleやhonto、BookLive!、GooglePlayを使っている。
さて、アップルの次の手はどうなっているのだろう。iPadはどう進化するのか。メモリは2GBでNexus 7やKindle Fire HDXの後を追うのか。64bitのCPUはどう活かされるのか。Macとの連携は・・・もうすぐ結果が出る。
iPad の減速を チャートで分析 : なぜ、iPhone のように成長できないのか? | Agile Cat --- in the cloudネタ。
その理由は、iPhoneは毎日持ち運んで頻繁に使うので傷みやすいし、話題性も高いので、2年も経てば交換したくなる可能性が高いということだろう。iPadはもちろん新しいほうがよいけど、まあ持っているというだけでインパクトがあるから、人にそれほど見せるものでもなく、2年に一度更新するほどの必要性はない。それでなくともiPhoneや通信料は高価なのだ。他のものを買い替える余裕はないかもしれない。それが答えだ。
それはともかく、予想外のiPadやiPad miniが登場するのか、どうか。大概、予想通りのマイナーバージョンアップに留まると思うが、iOSとOS Xの融合についてなんらかの進捗があるのかどうかに最も興味がある。そして、それがWindowsユーザーを引き付けるものかどうかが問題なのだ。Windows 8.1タブレットが成功する前に達成すべき課題だったのだが、間に合うだろうか。今年のクリスマス商戦で成功するのは何だろうか。
アップルイベント開催目前--発表が予想される7つのこと - CNET Japanの記事を見て思い出したのは、OS X Mavericksのこと。新しいiPadで動いたらおもしろいけどね。この記事は本質を突いた中味のあるよい記事だと思う。僕の個人的なご都合主義的予測を一般化する手法よりは素直に書いてある。浮かび上がる疑問にも共感できるね。iOSのアプリはMacでも動くようにするべきだろう。そうすると、Macはもっと売れるようになる。iBooksは動くようになるらしいが・・・
Apple to Offer Live Video Stream of Today's Media Event - Mac RumorsによるとApple TVにライブで配信されるというので、Apple TVを開くとしばらくして、Apple Eventsのアイコンが飛んできた。10:00AM PDT(Pacific Daylight Time)開始。+16時間でJSTになるから、10/23の2:00AMからだ。残念ながら起きている元気はない・・・明日帰宅してから録画を見るしかない。
iPad mini Retina は「買い」なのだろうか - W&R : Jazzと読書の日々ネタ。重要な論考。
既にiCloudは本格的に動き始めた。が、どういうレベルで使うかということになると、Apple TVのMusicぐらいだ。iWorkを本格的に使うことはすぐにはない。デスクトップでは、LibreOfficeがあるし、Google Docsもある。iPadをモバイルで本格的に使うようになれば価値があるかもしれない。しかしながら、ハイパーテキストの時代には、もうOfficeアプリは便利であっても古いものだと思うが。
コンピュータメディア環境において、今後の問題はすぐ見えてくる。様々なサービスで購入したものの統合的な管理だ。まだ完璧な解はない。しばらくないだろう。
『次は「iCloudから情報を引き出すデバイス」。「iWatch」が現実味を帯びてきました。そして、個人情報を一企業のデータベースに託すことの意味を考え直さないといけない。一瞬にして国境が消失し、巨大な「帝国」が出来上がる。Appleは「必然」を歩んでいる。』iCloudからiWatchを連想したところに感心した。ただ、ぼくらがAppleだけを使うことはあり得ないと思う。Appleにはもっと柔軟性が必要だ。もっとも柔軟なのはAmazonだ。どこにでも入り込める。まだ環境をコントロールするほどの力はないとも言えるが。我々はGoogleを含めてあらゆるサービスを使うことになるだろう。
アップル・ウオッチャーとして有名なJohn Gruberによる先日のスペシャルイベントの見方。
ちょっと感心したのは、iPad AirからiPad Proの登場を予想したこと。当たれば素晴らしい。20インチぐらいまでの大きめのタブレットはWindowsでは既にいろいろと出ている。レノボ Flex シリーズ発表。平置き対応デスクトップFlex 20、300度開くノートFlex 14 - Engadget Japaneseとか、HP ENVY Rove20 概要 | 日本HPとかね。
MacのiWorkが優先順位の高いiOS用の機能と合わせるためにダウングレードされていることについて言及している。また、iWork for iCloudはiWorkのWindows版という見方を示している。前者は知らなかったが、後者の見方は当然だろう。iWorkについてはLibreOffice/OpenOfficeのWindows/Mac双方における存在によって影響は限定的とも考えらるが、iPad販促の観点からは、iPadをWindowsと併用する場合にはiWorkは必要になるということだろう。iWorkそのものよりは、WindowsにおけるiTunesとiCloudの存在は大きいと思う。アップデートの止まったWindows用のSafariも今後は更新されるようになるかもしれない。
しかしながら、Windows8.1タブレットの価格/性能比によってはiTunes/iCloudも両刃の剣となる。それでも、iPadは売れなくともiPhoneは売れるだろう。それがApple生態系の強さだ。Apple Reports Q4 2013 Year-End Results: $7.5 Billion Profit on $37.5 Billion in Revenue [Call Concluded] - Mac Rumorsにある総収入のグラフを見れば、iPhoneの比率の大きさに驚くが、加えてiTunes Storeの存在感が最早無視できない状態になっていることを感じる。
HP Chromebook 11 announced - specs, price and details | Google Chrome: News, Reviews, Forum & Beyond。驚異的な価格、279ドル。
買うかなと心を揺り動かされる価格だが、冷静になろう。実際、そう簡単に使い道はないのだから。PCは持っているし、iPadもNexus 7もある。MacBookも持っているわけだし。それらができること以外のことができないと購入する意味はない。
おもちゃはいくらでもある。それらをいかに使いこなすかが問題なのである。いまのところ、iPadもNexus 7も真のおもちゃにはなりにくい。いずれもモバイル・ブラウザなのだ。Jailbreakする必要があるマシンには、わざわざリスクを冒して苦労してもしかたがないので、お客さんとして乗るしかない。根本的におもしろみに欠ける。
Chromebookはどうなんだろうねえ。Acer、初のHaswell搭載11.6インチChromebook「C720」を発表 - ITmedia ニュースは250ドルだ。CPUはHaswellでRAMは4GB、16GBのSSD搭載。バッテリー持続時間が8.5時間。Chromebookではローカルサーバーが動くという話はあるね。良く調べてみう。涎が出てくるかもしれない。
さて、RSコンポーネンツ、Raspberry Pi Model Aと専用カメラモジュールのセット | マイナビニュースのようなものも出ている。5000円でおつりがくるのだが、何に使うか、どのようにして使うかを想像する。I^(2)C - Wikipediaでカメラモジュールと通信する。
Googleのドライブストレージのページを見ると、GoogleドライブとGmailとGoogle+写真を合わせて15GB無料で使えることになっている。例えば、Gmailのページの下の「管理」のリンクから見ることができる。それぞれの容量も確認できるし、溢れそうになれば自分で調整可能だ。メールの管理をどうするかを考える必要がありそうだが、うまくできている。
ついでに確認すると、iCloudは5GBが無料。Amazon Cloud Driveも5GBが無料。SkyDriveが7GB。どれも本格的に使うには少し小さすぎる。仮置きで便利に使うぐらいしか役に立たないだろう。ちなみにDropboxは2GB-18GBが無料範囲。
肩の凝らない話題をいくつか。まず、シリコンバレー101 (535) ウエアラブルデバイスでAppleにチャンスを与えてしまったSamsung | マイナビニュースネタ。
まあね。時計で人を感心させるのは難しい。時刻を確認できればそれでいいよ。僕のはG-SHOCKだけど、世界時計で、電波時計で、ソーラーで、そして温度を測定表示できるアナログ針時計。電池を交換しなくて済むことを最低条件にできるだけ手間が掛からない時計を最近は選んでいる。もし、毎日コードをつないで充電が必要としたらよほど凄いことができないとダメだよ。パイロット(とそれにあこがれる人)向けのGPS腕時計 ≪ WIRED.jpのような時計もある。Appleも機能的な面ではチャンスを持て余すだろう。機能的には限りなくシンプルになる。ただ、デザインの側面では、Jony Ive's custom-designed Leica camera took nine months and 55 engineers to build | The Vergeのようなことはあるかもしれない。優美というにふさわしい形態だ。One-offで6百万ドル掛けている。デザインの力がどこまで及ぶか。第3世代のiPod touchを見て、これは本当に工業製品なのだろうか、手のひらにしっくりと馴染む工芸品ではと思わせた力がアップルにはある。世界の時計メーカーを震撼させる製品が生み出されるのかどうかだ。
シリコンバレー101 (534) 優れた製品ならくつ下も売る、物販に乗り出したEvernoteの狙いは? | マイナビニュースネタ。これはいいかもね。結局、どれだけ手間を掛けることができるか、掛けたくなるかということが問題なんだ。それで、今のところは、Evernoteは試しにデータを送っておくぐらいでそれほど使っていない。更新日記があるからね。それを押し退ける力を持っているとすれば相当のものだよ。やはり作り出す部分が足りないかな。少なくとも収集する情報は従の扱いにならないと。いや、使うつもりで調べてみると、ノートとしてもよくできているね。Webブラウザで動作するクラウドアプリとしてもすごい。最近は、Chromeのアプリランチャーから起動するので触ることが多くなっている。便利。
シリコンバレー101 (533) 「iTunes Radio」はPandoraやSpotifyに勝てるか? | マイナビニュースネタ。シンプル・イズ・ベスト。最近は、アップルだけでなく日本に届かないサービスや製品が多いのが残念だね。
アップルネタが二つだったが、予想以上のことができないアップルが批判されている。予想なんて誰でもできるけど、実行するのは大変なことを忘れてはいけない。iOSニューデバイスにおけるiWorkの無料化なんて地道だけど効果的な処置だよね。Macで購入すれば、iOS版は無料だったような気もするが、今やMacユーザーでないAppleユーザーは大変多いだろう。iTunes FestivalのLady GaGaとElton Johnは楽しめた。コンテンツや文化もあるけど、やはり、Appleの真骨頂はデザインにある。最近は、iPad 2の縦横比のバランスの良さとiOS 7を眺めながらそう思う日々を過ごしている。iOS 7によって洗練された新しいマシンとして甦ったね。
「OS X無料化」が意味する新時代 ≪ WIRED.jp、Apple が選んだハイ・リスク戦略 : iPad Mini の価格が理解できない | Agile Cat --- in the cloudとIntel が強気に発言 : $99 タブレットと、$299 Haswell ラップトップ、$349 ハイブリッドが登場する! | Agile Cat --- in the cloudネタ。台風27号の影響で行きも帰りも大雨、外気温も18℃。明日未明は特に大雨との予測。大雨警報が出ている。被害が出なければよいが。
アップルのスペシャルイベントを見ながら、iPad 2とMacBookをアップデートした。OS X Mavericksは無料だが、当然という感じ。Windowsだって基本的にはハードウェアに付いてくるもので、OSとして購入するものではない。そして、旧機種のMacBookではAirPlayのフルの機能は使えないし、iWorkやGarageBandは自腹で購入したのじゃなかったっけ。いずれも数百円から千円までという価格だが。昔のように何万円もする時代は過ぎ去った。iMovieは購入した記憶はないけど、インストールしてある。でも近いうちに使う機会はないだろう。
結局だね。ユーザーの時間とお金をどう奪い合うかというゲームなんだよね。しかし、ふつうは時間もお金もないというのが現実だから、お金のあるところから取ろうというのがアップルの戦略だ。それほど使いもしないのにお金があるから買うという層にターゲットを合わせたんだろう。というのは、そろそろタブレットも成熟してきているからだ。そういう読みだろう。僕もあまり新製品には興味がない。使うアプリはどのOSにもあるというか、ブラウザがあれば事足りるのだ。今後、アピールする製品の焦点が移る可能性があるのかもしれない。
そのような時に、Wintelタブレットの登場だ。高性能・低価格でWindows文化圏を盛り上げようということになる。いや、売れるかもしれないね。価格は最重要だ。iTunesはまだWindowsをサポートしているが、Safariはアップデートされていない。まあ、iCloudは動くから問題ない。でも、iPad/Androidユーザーが振り向いてくれるかどうかが問題だ。間に合っていますと言うかもしれない。買い替える時がチャンスであるにしても。
さて、Appleはソフトウェアよりはハードウェアにこだわって作っているメーカーだ。ちょっと家電的。使い方を決めてくる。そこがおもしろくないところではある。でも、新しいものを取り込むセンスは抜群だ。Ajax、Podcast/RSS、ePub、Canvas。そこまではいいんだが・・・
Wintel、古き良き友よ。自由のタブレットを早く出してくれ。
その他の注目デバイス。いろいろとあるねえ。「当初NFCは非搭載だった」HDR-MV1やDSC-QX100の開発秘話のカメラをリアカメラのない旧Nexus 7と組み合わせるってアイデアはいいかもしれない。DSC-QX10 | デジタルスチルカメラ Cyber-shot“サイバーショット” | ソニーなら、24980円。
Camera Remote Apps Developer Programに一応登録してきた。
【CEATEC2013】Intel、カメラを利用した3次元認識デモなどを披露 | マイナビニュースやIntel、組込用SoC「Quark」搭載のArduino互換開発ボード「Galileo」を発表 | マイナビニュースを見ると、Intelも元気だ。
Dell、Bay Trail/Windows 8.1搭載の8型タブレットなど - PC WatchとDell、2,560×1,440ドット表示対応の11.6型“2-in-1”Ultrabook - PC Watch。2-in-1は高いね。
次から次へとおもしろいデバイスが発表され始めた。まあねえ、あまり時間が取れないから、ややこしいのは手が出しにくい。
Fitbit Forceハンズオン:今度は期待通りかも! : ギズモード・ジャパン。
時計じゃないけど、時刻表示はできる。電池は10日持つ。写真に表示されているMIという単位はマイルと思われる。
今日はボランティアの仕事?で昼からひろしま文化科学館にいた。無事役目を果たして、紙屋町そごう6階の紀伊國屋書店に向かい、JavaScriptなどの本を全部調べた。オライリーの「HTML5 Canvas」という本がかなりいいなあと思った。まあ、問題を解決したばかりだし、Canvasだけわかっても仕方がないしということもあるが、次のステップで買うかも。詳しい。内表紙に「To Flash」とあった。主となるテクノロジーも時代とともに変化する。
O'Reilly Japan - 初めてのPHP、MySQL、JavaScript&CSS 第2版も、PHPとJavaScriptが一冊の本にあるので興味が出て調べたが、JavaScriptの重要性が高まっているということに過ぎないという感じがした。JavaScriptの存在形態はPHPに似ているといっても逆さまな感じもするわけだけど、結局は他のプログラミング言語と同様に競合していく運命にあると思うのだが。
最近は、Perlの本の存在感がないので、残念。僕が最近注目したのは、8月に出たO'Reilly Japan - 続・初めてのPerl 改訂第2版だ。最新のPerl(正確にはv5.14)に対応している。原題は「Intermediate Perl, 2nd Edition」だから、中級者用ということ。「Programming Perl 4th edition Covers Version 5.14」(英語版原書で1130ページ)の邦訳がないだけに「初めてのPerl」しか持っていないなら、432ページ、3,780円は、中味は大変濃いのでお買い得な感じだ。目次を見直していると欲しくなってしまったよ。Perlは初級者でも十分に複雑な現実の問題を解決できる能力を持てるわけだけど、本書の思わせぶりな各章や項目のタイトルを見ていると、上級者の本ってどんなになるんだろうと思ってしまう。是非、出してほしいね。「Programming Perl」やDOMINUSの「HIGHER-ORDER PERL」はあるにしても。
NQP gets MoarVM support, cursor reduction, and other news | 6gutsは、Rakudo on JVMの現況を知らせている。Parrotが到達しているspectestsの99.9%はクリアしたが、UTF-8周りにまだ問題が残っているらしい。Rakudoが実用的に動いたとして、使いこなせるかどうかというよりも、使うべき問題を持ち合わせているかどうかなんだよね。
今日、帰りにガソリンを入れようとすると、価格が上昇していた。158円/リットル。専用カードで3円値引きで、Tポイントが付く。なぜ上がったのと聞くと、わからないという。いつもわからないと言うので、今回は、よく聞いとかないと、客に説明できないでしょうと言うと、すみません、次はよく聞いておきますと言って、ティッシュを五つくれた。何でも言いたいことは言ってみるもんだと思った。後でよく考えると2円/リットル上がっただけだった。外気温、16℃。冷えてきた。室温も窓を閉めていても26℃を少し切っている。台風26号も寒気に押し出されたか。伊豆大島や首都圏は土砂崩れなどで相当な被害が出ている。
まず、Amazonネタ。Amazon、Snapdragon 800/高解像度液晶搭載のKindle Fire HDXを国内販売 ~15,800円のKindle Fire HD 7型モデルも - PC Watch。HDXは7インチ16GBが24,800円。8.3インチ16GBは、800万画素背面カメラ付きで39,800円。2.2GHzクアッドコアの2GBマシン。これまで、Kindle Fireがそれほど大きな成功を収めたという話はないが、グレードアップ機を着実に出している。
もう一つのAmazonネタは、アマゾン、HTCと共同で複数のスマートフォンを開発中か - CNET Japanだ。さて、どうなるのだろう。Apple、Googleを上回る魅力をどう打ち出すのか。
一番印象に残ったのは、【Windows 8.1カウントダウン】発売直前。一般公開までに知っておきたいWindows 8.1 - PC Watch。マイクロソフトの認識として「現状としてスマートフォンとタブレットが急成長していることを示し、今後のITデバイスがモバイル化することは必然であるとした。今、デマンドはデバイスを手に持って歩けることであり、同社もまた、今、モバイルにベット(賭ける)するという。そのためにも、タブレットとレガシーなPCの垣根を取り払うことは重要で、PCやタブレットという定義自体を変えることも考えなければならないという。」が示されていること。
レノボの動き、レノボ、モバイル13.3型ディスプレイ「ThinkVision LT1423p」 ~タッチ/ペン操作対応、ThinkCentre M73新モデルも - PC Watchとレノボ、リモートアクセスも可能なSOHO/個人向けNAS - PC WatchがITデバイスの新しい動向を示唆しているように見えてきた。確かに、もう図体のでかいPCの時代は終わりかもしれない。そして、これまでPCの中心的ユーザーではなかった人たちがスマホやタブレットのユーザーとしてiTデバイスの世界に侵入しつつある。タッチ/ペン操作が何を変えるのだろう。モバイル13.3型ディスプレイ「ThinkVision LT1423p」は、モバイルにしては少し大きすぎるような気もするが、筆圧256段階が表現できるデジタイザペンが使える。enchantMOONのエミュレーション用デバイスとして考えられるかもしれない。価格が問題だが・・・NASもモバイルITデバイスの大容量ストレージ・デバイスとして必須になる可能性があると思う。
取り敢えず、今日はここらへんで。おやすみなさい。
enchantmoonstrokes.appspot.comを参考に始めたのだが、backingのstrokeの描画はともかく、加えて、複数のstickerのstrokeや画像とclipの組み合わせをどのように表現すればよいのかが問題だ。画像を組み込む - Canvas - HTML5.JPやcanvas要素の基本的な使い方まとめを元に試行錯誤している。もちろんうまく描画できるページもあるが、stickerの数が増えるとWebへのリンクは生きているがWebページから切り出した画像が表示されない問題がある。画像が一つの場合には、clipのデータを活かして、Webページから切り出した画像を表示できるようになった。
現時点のpageview.cgiを載せておこう。単に順列組合せを試すだけでは問題は解決しそうもない。
#!/Perl64/bin/perl.exe # use CGI qw(:cgi); use JSON; # imports encode_json, decode_json, to_json and from_json. my $server = "http://xxx.yyy.zzz.aaa/cgi-bin/enchantMOON"; my $emoondir = "C:/xxxxx/yyyyyy/enchantMOON/Data/MyNotebook1"; (my $thisscript = $0) =~ s/^.+[\\\/]([^\\\/]+)$/$1/; print <<"CANVASTOP"; Content-type: text/html <html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta charset="UTF-8"> <style> body { width: 100%; background: #000000; } .footer { font-size: 80%; color: white; text-align: right; margin-top: 10pt; text-align: right; } .footer a { color: white; text-decolation: none; } #canvas { border: 0px; padding: 0p; } #frame { display: hidden; border: 0px; padding: 0p; } </style> </head> <body> <div style="width: 768px; margin: auto;"> <div id="frame"> <canvas width=""768"" height=""1024"" id="canvas"></canvas> </div> </div> CANVASTOP #### info.json, manifest.json my $json = JSON->new->allow_nonref; #### page my $page = param('page'); unless($page){ $page = "0PQQ21081376620923240"; } my $pageinfo = ""; %page_scalar = (); open my $fh, $emoondir . "/$page/info.json"; $pageinfo = <$fh>; close $fh; $page_scalar{$page} = $json->decode( $pageinfo ); ### backing my $backing = $page_scalar{$page}->{backing}; my $backinginfo = ""; my %backinginfo_scalar = (); open my $fh, $emoondir . "/$page/$backing/info.json" or die "Can't open it!?: $!"; $backinginfo = <$fh>; close $fh; $backinginfo_scalar{$backing} = $json->decode( $backinginfo ); my $backingcolorhex = ""; my $strokecolorhex = ""; my $clipcolorhex = ""; my $linked_page = ""; my $c = 0; my $s = 0; my $i = -1; my $j = -1; my $c2 = 0; my $s2 = 0; my $k = -1; ## stroke while($backinginfo_scalar{$backing}->{strokes}->[++$i]){ ($backingcolorhex = sprintf "%X", $backinginfo_scalar{$backing}->{strokes}->[$i]->{color}) =~ s/^FFFFFFFFFF(\w{6})$/"#$1"/; print <<"BACKINGBEGIN"; <script> window.addEventListener("load", function(){ var canvas = document.getElementById("canvas"); var context = canvas.getContext("2d"); context.beginPath(); BACKINGBEGIN while($backinginfo_scalar{$backing}->{strokes}->[$i]->{data}->[++$j]){ ++$c;++$s; if($s == 1){ print "context.moveTo($backinginfo_scalar{$backing}->{strokes}->[$i]->{data}->[$j],"; }elsif($c == 1){ print "context.lineTo($backinginfo_scalar{$backing}->{strokes}->[$i]->{data}->[$j],"; }elsif($c == 2){ print " $backinginfo_scalar{$backing}->{strokes}->[$i]->{data}->[$j]);\n"; }elsif($c == 3){ print "context.globalAlpha = $backinginfo_scalar{$backing}->{strokes}->[$i]->{data}->[$j];\n"; $c = 0; next; } } $c = 0;$s = 0;$j = -1; print <<"BACKINGEND"; context.strokeStyle = $backingcolorhex; context.lineWidth = $backinginfo_scalar{$backing}->{strokes}->[$i]->{width}; context.stroke(); }, false); </script> BACKINGEND } $i = -1; #### stickers my $sticker = ""; my $stickerinfo = ""; my $manifest = ""; my %stickerinfo_scalar = (); my %manifest_scalar = (); while($sticker = $page_scalar{$page}->{stickers}->[++$i]){ open my $fh, $emoondir . "/$page/$sticker/info.json" or die "Can't open $sticker!?: $!"; $stickerinfo = <$fh>; close $fh; $stickerinfo_scalar{$sticker} = $json->decode( $stickerinfo ); open my $fh, $emoondir . "/$page/$sticker/manifest.json" or die "Can't open $sticker!?: $!"; $manifest = <$fh>; close $fh; $manifest_scalar{$sticker} = $json->decode( $manifest ); } $i = -1; ### sticker foreach $sticker (sort keys %stickerinfo_scalar){ ## stroke while($stickerinfo_scalar{$sticker}->{strokes}->[++$i]){ ($strokecolorhex = sprintf "%X", $stickerinfo_scalar{$sticker}->{strokes}->[$i]->{color}) =~ s/^FFFFFFFFFF(\w{6})$/"#$1"/; print <<"STROKEBEGIN"; <script> window.addEventListener("load", function(){ var canvas = document.getElementById("canvas"); var context = canvas.getContext("2d"); context.beginPath(); STROKEBEGIN while($stickerinfo_scalar{$sticker}->{strokes}->[$i]->{data}->[++$j]){ ++$c;++$s; if($s == 1){ print "context.moveTo($stickerinfo_scalar{$sticker}->{x} + $stickerinfo_scalar{$sticker}->{strokes}->[$i]->{data}->[$j],"; }elsif($c == 1){ print "context.lineTo($stickerinfo_scalar{$sticker}->{x} + $stickerinfo_scalar{$sticker}->{strokes}->[$i]->{data}->[$j],"; }elsif($c == 2){ print " $stickerinfo_scalar{$sticker}->{y} + $stickerinfo_scalar{$sticker}->{strokes}->[$i]->{data}->[$j]);\n"; }elsif($c == 3){ print "context.globalAlpha = $stickerinfo_scalar{$sticker}->{strokes}->[$i]->{data}->[$j];\n"; $c = 0; next; } } $c = 0;$s = 0;$j = -1; print <<"STROKE"; context.strokeStyle = $strokecolorhex; context.lineWidth = $stickerinfo_scalar{$sticker}->{strokes}->[$i]->{width}; context.stroke(); STROKE if($manifest_scalar{$sticker}->{linked_pages}->[0]){ # print $manifest_scalar{$sticker}->{linked_pages}->[0],"\n"; if($manifest_scalar{$sticker}->{linked_pages}->[0] =~ /^[\w\d_]{21}$/){ $linked_page = "$server/$thisscript?page=$manifest_scalar{$sticker}->{linked_pages}->[0]"; }else{ $linked_page = "$manifest_scalar{$sticker}->{linked_pages}->[0]"; } print <<"ACCESS"; canvas.addEventListener("mousedown", mouseDownListner, false); function mouseDownListner(e) { var rect = e.target.getBoundingClientRect(); mouseX1 = e.clientX - rect.left; mouseY1 = e.clientY - rect.top; if (mouseX1 > $stickerinfo_scalar{$sticker}->{x} && mouseX1 < $stickerinfo_scalar{$sticker}->{x} + $stickerinfo_scalar{$sticker}->{width}) { if (mouseY1 > $stickerinfo_scalar{$sticker}->{y} && mouseY1 < $stickerinfo_scalar{$sticker}->{y} + $stickerinfo_scalar{$sticker}->{height}) { location.href = "$linked_page"; } } } ACCESS $linked_page = ""; } print <<"STROKEEND"; }, false); </script> STROKEEND } $i = -1; ## clip if($stickerinfo_scalar{$sticker}->{clip}->{data}->[0]){ ($clipcolorhex = sprintf "%X", $stickerinfo_scalar{$sticker}->{clip}->{color}) =~ s/^FFFFFFFFFF(\w{6})$/"#$1"/; print <<"CLIPBEGIN"; <script> window.addEventListener("load", function(){ var canvas = document.getElementById("canvas"); var context = canvas.getContext("2d"); // context.globalAlpha = 0; context.beginPath(); context.rect($stickerinfo_scalar{$sticker}->{x}, $stickerinfo_scalar{$sticker}->{y}, $stickerinfo_scalar{$sticker}->{x} + $stickerinfo_scalar{$sticker}->{width}, $stickerinfo_scalar{$sticker}->{y} + $stickerinfo_scalar{$sticker}->{height}); context.fillStyle = $clipcolorhex; context.fill(); context.beginPath(); CLIPBEGIN while($stickerinfo_scalar{$sticker}->{clip}->{data}->[++$k]){ ++$c2;++$s2; if($s2 == 1){ print "context.moveTo($stickerinfo_scalar{$sticker}->{x} + $stickerinfo_scalar{$sticker}->{clip}->{data}->[$k],"; }elsif($c2 == 1){ print "context.lineTo($stickerinfo_scalar{$sticker}->{x} + $stickerinfo_scalar{$sticker}->{clip}->{data}->[$k],"; }elsif($c2 == 2){ print " $stickerinfo_scalar{$sticker}->{y} + $stickerinfo_scalar{$sticker}->{clip}->{data}->[$k]);\n"; }elsif($c2 == 3){ print "context.globalAlpha = $stickerinfo_scalar{$sticker}->{clip}->{data}->[$k];\n"; $c2 = 0; next; } } $c2 = 0;$s2 = 0;$k = -1; print <<"CLIPEND"; context.closePath(); context.lineWidth = $stickerinfo_scalar{$sticker}->{clip}->{width}; context.clip(); }, false); </script> CLIPEND } ## image if($stickerinfo_scalar{$sticker}->{image}){ print<<"IMAGEBEGIN"; <script> window.addEventListener("load", function(){ var canvas = document.getElementById("canvas"); var context = canvas.getContext("2d"); // context.globalAlpha = 1; var img = new Image(); img.src = "/enchantMOON/Data/MyNotebook1/${page}/${sticker}/$stickerinfo_scalar{$sticker}->{image}"; img.onload = function() { context.drawImage(img, $stickerinfo_scalar{$sticker}->{x}, $stickerinfo_scalar{$sticker}->{y}); }; IMAGEBEGIN if($manifest_scalar{$sticker}->{linked_pages}->[0]){ # print $manifest_scalar{$sticker}->{linked_pages}->[0],"\n"; if($manifest_scalar{$sticker}->{linked_pages}->[0] =~ /^[\w\d_]{21}$/){ $linked_page = "$server/$thisscript?page=$manifest_scalar{$sticker}->{linked_pages}->[0]"; }else{ $linked_page = "$manifest_scalar{$sticker}->{linked_pages}->[0]"; } print <<"ACCESS"; canvas.addEventListener("mousedown", mouseDownListner, false); function mouseDownListner(e) { var rect = e.target.getBoundingClientRect(); mouseX1 = e.clientX - rect.left; mouseY1 = e.clientY - rect.top; if (mouseX1 > $stickerinfo_scalar{$sticker}->{x} && mouseX1 < $stickerinfo_scalar{$sticker}->{x} + $stickerinfo_scalar{$sticker}->{width}) { if (mouseY1 > $stickerinfo_scalar{$sticker}->{y} && mouseY1 < $stickerinfo_scalar{$sticker}->{y} + $stickerinfo_scalar{$sticker}->{height}) { location.href = "$linked_page"; } } } ACCESS $linked_page = ""; } print <<"IMAGEEND"; }, false); </script> IMAGEEND } } print <<"CANVASEND"; </body> </html> CANVASEND
この方向性にすべてを解決する道があるかどうかは定かでない。むしろ、Processing.jsを使うとかを考えた方が確実かも。しかし、Processing.jsを使ってもcanvas描画はブラウザによって不安定な感じもあるので、当面は茨の道。clip()を使ったJavaScriptではenchantMOONのブラウザでは描画されない。clip()未サポートというのは本当だった。
paperMOON on iPad仕方がないので、iPadで実行して画面サンプル取得。Webページは切り取った形状に表示されているだけでなく、切り取ったイメージ上にデジタルペンで書き込んだ文字も再現されている。一つのstickerのinfo.jsonに存在するstroke、clip、imageのデータがすべて取り込まれている例。もちろん、画像をクリックすれば更新日記を表示する。
画像の外にある文字は、backingのinfo.jsonのstrokeデータの描画になる。
今日は画像サンプルを取ろうかとiPadを起動するとWiFiにつながらない。他のデバイスでは無線LANは動作している。壊れたかと焦ったね。電源を落として再起動で復活。やれやれ。初めてだよ、こんなこと。iOS 7は大丈夫かな。
今日はいろいろなことが起きる。Chromeではclip()を生成するpageview.cgiは真っ黒な画面で画像は何も表示されなくなった。FirefoxやSafariは問題ない。IE10も表示可能。Chromeのcanvasの表示は鬼門だ。うまく表示されることもあるからややこしい。更新日記のProcessing.jsによるcanvas描画も表示できる日とできない日がある。キャッシュか何かの関係だろうか。
それはともかく、「クリッピングパスの領域の外側は透明として扱われますので、結果的に、切り取り窓の外側は、一番最初に塗りつぶした背景色の黒が表示されることになります。」ということなので、クリッピングパスが二つあると交差した領域だけの画像が残ることになるのではと思う。他の部分は画像自体も透明になってしまうから見えなくなると考えると現在の現象を説明できる。これを思いついたのは、交差した部分の画像だけが表示されているページがあったからだ。二つの画像が交差していないページは両方の画像が見えなくなる。どうもこれが正解のようだ。したがって、二つの画像を一つの画像に合成して、クリップ領域も一つにまとめる必要があるか、別のcanvasに描いて重ねるかということになるかな。
HTML5 の Canvas を使って素晴らしいグラフィックスを作成するにあるとおり、canvas関係のidを別にすればすべての要素を表示させることができるが、それぞれのcanvasは別の位置に表示されてしまう。重ねるのはどうするのだろう。明日はHTML5 canvas のパフォーマンスを向上させる - HTML5 Rocksを調べてみるか。ヒントはz-index。
canvasのレイヤーを作ることは簡単にできるが、ハイパーリンクは最上位のレイヤーのリンクしか機能しないという副作用があるみたいだな。そうすると、やはりキャンバスを並べる方向性が正解のような感じだけど、backingのレイヤーの上にそれぞれのstickerの部分的なcanvasのレイヤーを並べるということになりそうだ。なるほど、そういうことか。言葉の意味が出てきたね。ただ、プログラムの構造はだいぶ変更する必要があるな。
backingとstickersのレイヤーを重ねることでページの描画はほぼ期待通り。ハイパーリンクも完璧に効いている。
さて、なんとか動かせたね。info.jsonの描画とmanifest.jsonのlinked_pagesへのハイパーリンクをWebブラウザ上でほぼ実現した。backingとstickersのcanvasをレイヤーに分けることで実現できた。多少相違点は残っている。しかし、これである程度、canvasはわかったし、JavaScriptの書き方や動かし方を具体的・実用的に修得できたのは収穫だった。とりあえず、Perlスクリプトを載せておこう。CGIといっても、ほとんどはJavaScriptを生成する。
#!/Perl64/bin/perl.exe # use CGI qw(:cgi); use JSON; # imports encode_json, decode_json, to_json and from_json. my $server = "http://xxx.yyy.zzz.aaa/cgi-bin/enchantMOON"; my $emoondir = "X:/xxxxx/yyyyyy/enchantMOON/Data/MyNotebook1"; (my $thisscript = $0) =~ s/^.+[\\\/]([^\\\/]+)$/$1/; #### info.json, manifest.json my $json = JSON->new->allow_nonref; #### page my $page = param('page'); unless($page){ $page = "0PQQ21081376620923240"; } my $pageinfo = ""; %page_scalar = (); open my $fh, $emoondir . "/$page/info.json"; $pageinfo = <$fh>; close $fh; $page_scalar{$page} = $json->decode( $pageinfo ); ### backing my $backing = $page_scalar{$page}->{backing}; my $backinginfo = ""; my %backinginfo_scalar = (); open my $fh, $emoondir . "/$page/$backing/info.json" or die "Can't open it!?: $!"; $backinginfo = <$fh>; close $fh; $backinginfo_scalar{$backing} = $json->decode( $backinginfo ); #### stickers my @stickers = (); my $sticker = ""; my $i = -1; my $stickerinfo = ""; my $manifest = ""; my %stickerinfo_scalar = (); my %manifest_scalar = (); while($sticker = $page_scalar{$page}->{stickers}->[++$i]){ push @stickers, $sticker; open my $fh, $emoondir . "/$page/$sticker/info.json" or die "Can't open $sticker!?: $!"; $stickerinfo = <$fh>; close $fh; $stickerinfo_scalar{$sticker} = $json->decode( $stickerinfo ); open my $fh, $emoondir . "/$page/$sticker/manifest.json" or die "Can't open $sticker!?: $!"; $manifest = <$fh>; close $fh; $manifest_scalar{$sticker} = $json->decode( $manifest ); } $i = -1; print <<"CANVASTOP"; Content-type: text/html <html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta charset="UTF-8"> <style> body { width: 100%; background: #000000; } #canvas { border: 0px; } #frame { border: 0px; } </style> </head> <body> <div style="width: 768px; margin: auto; position: relative"> <div id="frame"> CANVASTOP my %canvas = (); my %context = (); print "\t\t<canvas width="\"$backinginfo_scalar{$backing}->{width}\"" height="\"$backinginfo_scalar{$backing}->{height}\"" id=\"$backing\" style=\"position: absolute; left: 0; top: 0; z-index: 0;\"></canvas>\n"; $canvas{$backing} = "canvas0"; $context{$backing} = "context0"; $i = 0; my $j = 0; my $k = 0; while($sticker = shift @stickers){ print "\t\t<canvas width="\"$stickerinfo_scalar{$sticker}->{width}\"" height="\"$stickerinfo_scalar{$sticker}->{height}\"" id=\"$sticker\" style=\"position: absolute; left: $stickerinfo_scalar{$sticker}->{x}; top: $stickerinfo_scalar{$sticker}->{y}; z-index: ",++$k, ";\"></canvas>\n"; $canvas{$sticker} = "canvas" . (++$i); $context{$sticker} = "context" . (++$j); } $i = -1;$j = -1;$k = -1; print <<"CANVAS"; </div> </div> CANVAS my $backingcolorhex = ""; my $strokecolorhex = ""; my $clipcolorhex = ""; my $linked_page = ""; my $c = 0; my $s = 0; my $c2 = 0; my $s2 = 0; ## stroke while($backinginfo_scalar{$backing}->{strokes}->[++$i]){ ($backingcolorhex = sprintf "%X", $backinginfo_scalar{$backing}->{strokes}->[$i]->{color}) =~ s/^FFFFFFFFFF(\w{6})$/"#$1"/; print <<"BACKINGBEGIN"; <script> window.addEventListener("load", function(){ var $canvas{$backing} = document.getElementById("$backing"); var $context{$backing} = $canvas{$backing}.getContext("2d"); $context{$backing}.beginPath(); BACKINGBEGIN while($backinginfo_scalar{$backing}->{strokes}->[$i]->{data}->[++$j]){ ++$c;++$s; if($s == 1){ print "$context{$backing}.moveTo($backinginfo_scalar{$backing}->{strokes}->[$i]->{data}->[$j],"; }elsif($c == 1){ print "$context{$backing}.lineTo($backinginfo_scalar{$backing}->{strokes}->[$i]->{data}->[$j],"; }elsif($c == 2){ print " $backinginfo_scalar{$backing}->{strokes}->[$i]->{data}->[$j]);\n"; }elsif($c == 3){ print "$context{$backing}.lineWidth = $backinginfo_scalar{$backing}->{strokes}->[$i]->{width} * $backinginfo_scalar{$backing}->{strokes}->[$i]->{data}->[$j];\n"; $c = 0; next; } } $c = 0;$s = 0;$j = -1; print <<"BACKINGEND"; $context{$backing}.strokeStyle = $backingcolorhex; $context{$backing}.globalAlpha = 1.0; $context{$backing}.stroke(); }, false); </script> BACKINGEND } $i = -1; my $l = -1; ### sticker while($sticker = $page_scalar{$page}->{stickers}->[++$l]){ ## clip if($stickerinfo_scalar{$sticker}->{clip}->{data}->[0]){ ($clipcolorhex = sprintf "%X", $stickerinfo_scalar{$sticker}->{clip}->{color}) =~ s/^FFFFFFFFFF(\w{6})$/"#$1"/; print <<"CLIPBEGIN"; <script> window.addEventListener("load", function(){ var $canvas{$sticker} = document.getElementById("$sticker"); var $context{$sticker} = $canvas{$sticker}.getContext("2d"); $context{$sticker}.beginPath(); $context{$sticker}.rect($stickerinfo_scalar{$sticker}->{x}, $stickerinfo_scalar{$sticker}->{y}, $stickerinfo_scalar{$sticker}->{width}, $stickerinfo_scalar{$sticker}->{height}); $context{$sticker}.fillStyle = $clipcolorhex; $context{$sticker}.fill(); $context{$sticker}.beginPath(); CLIPBEGIN while($stickerinfo_scalar{$sticker}->{clip}->{data}->[++$k]){ ++$c2;++$s2; if($s2 == 1){ print "$context{$sticker}.moveTo($stickerinfo_scalar{$sticker}->{clip}->{data}->[$k],"; }elsif($c2 == 1){ print "$context{$sticker}.lineTo($stickerinfo_scalar{$sticker}->{clip}->{data}->[$k],"; }elsif($c2 == 2){ print " $stickerinfo_scalar{$sticker}->{clip}->{data}->[$k]);\n"; }elsif($c2 == 3){ print "$context{$sticker}.lineWidth = $stickerinfo_scalar{$sticker}->{clip}->{width} * $stickerinfo_scalar{$sticker}->{clip}->{data}->[$k];\n"; $c2 = 0; next; } } $c2 = 0;$s2 = 0;$k = -1; print <<"CLIPEND"; $context{$sticker}.closePath(); $context{$sticker}.globalAlpha = 0.5; $context{$sticker}.clip(); }, false); </script> CLIPEND } ## image if($stickerinfo_scalar{$sticker}->{image}){ print<<"IMAGEBEGIN"; <script> window.addEventListener("load", function(){ var $canvas{$sticker} = document.getElementById("$sticker"); var $context{$sticker} = $canvas{$sticker}.getContext("2d"); var img = new Image(); img.src = "/enchantMOON/Data/MyNotebook1/${page}/${sticker}/$stickerinfo_scalar{$sticker}->{image}"; img.onload = function() { $context{$sticker}.drawImage(img, 0, 0); }; IMAGEBEGIN if($manifest_scalar{$sticker}->{linked_pages}->[0]){ if($manifest_scalar{$sticker}->{linked_pages}->[0] =~ /^[\w\d_]{21}$/){ $linked_page = "$server/$thisscript?page=$manifest_scalar{$sticker}->{linked_pages}->[0]"; }else{ $linked_page = "$manifest_scalar{$sticker}->{linked_pages}->[0]"; } print <<"ACCESS"; $canvas{$sticker}.addEventListener("mousedown", mouseDownListner, false); function mouseDownListner(e) { var rect = e.target.getBoundingClientRect(); mouseX1 = e.clientX - rect.left; mouseY1 = e.clientY - rect.top; if (mouseX1 > 0 && mouseX1 < $stickerinfo_scalar{$sticker}->{width}) { if (mouseY1 > 0 && mouseY1 < $stickerinfo_scalar{$sticker}->{height}) { location.href = "$linked_page"; } } } ACCESS $linked_page = ""; } print <<"IMAGEEND"; }, false); </script> IMAGEEND } ## stroke while($stickerinfo_scalar{$sticker}->{strokes}->[++$i]){ ($strokecolorhex = sprintf "%X", $stickerinfo_scalar{$sticker}->{strokes}->[$i]->{color}) =~ s/^FFFFFFFFFF(\w{6})$/"#$1"/; print <<"STROKEBEGIN"; <script> window.addEventListener("load", function(){ var $canvas{$sticker} = document.getElementById("$sticker"); var $context{$sticker} = $canvas{$sticker}.getContext("2d"); $context{$sticker}.beginPath(); STROKEBEGIN while($stickerinfo_scalar{$sticker}->{strokes}->[$i]->{data}->[++$j]){ ++$c;++$s; if($s == 1){ print "$context{$sticker}.moveTo($stickerinfo_scalar{$sticker}->{strokes}->[$i]->{data}->[$j],"; }elsif($c == 1){ print "$context{$sticker}.lineTo($stickerinfo_scalar{$sticker}->{strokes}->[$i]->{data}->[$j],"; }elsif($c == 2){ print " $stickerinfo_scalar{$sticker}->{strokes}->[$i]->{data}->[$j]);\n"; }elsif($c == 3){ print "$context{$sticker}.lineWidth = $stickerinfo_scalar{$sticker}->{strokes}->[$i]->{width} * $stickerinfo_scalar{$sticker}->{strokes}->[$i]->{data}->[$j];\n"; $c = 0; next; } } $c = 0;$s = 0;$j = -1; print <<"STROKE"; $context{$sticker}.strokeStyle = $strokecolorhex; $context{$sticker}.globalAlpha = 1.0; $context{$sticker}.stroke(); STROKE if($manifest_scalar{$sticker}->{linked_pages}->[0]){ if($manifest_scalar{$sticker}->{linked_pages}->[0] =~ /^[\w\d_]{21}$/){ $linked_page = "$server/$thisscript?page=$manifest_scalar{$sticker}->{linked_pages}->[0]"; }else{ $linked_page = "$manifest_scalar{$sticker}->{linked_pages}->[0]"; } print <<"ACCESS"; $canvas{$sticker}.addEventListener("mousedown", mouseDownListner, false); function mouseDownListner(e) { var rect = e.target.getBoundingClientRect(); mouseX1 = e.clientX - rect.left; mouseY1 = e.clientY - rect.top; if (mouseX1 > 0 && mouseX1 < $stickerinfo_scalar{$sticker}->{width}) { if (mouseY1 > 0 && mouseY1 < $stickerinfo_scalar{$sticker}->{height}) { location.href = "$linked_page"; } } } ACCESS $linked_page = ""; } print <<"STROKEEND"; }, false); </script> STROKEEND } $i = -1; } print <<"CANVASEND"; </body> </html> CANVASEND
次はenchantMOONのstickerの中でもシール(プログラムとしてのhack.js)を動かしたいわけだが、これは実際上ハードウェアあるいはMOONPhase OSの機能をエミュレートする必要があるわけで、難易度は高い。まずはMOON.jsなどを調べてみるかな・・・
strokeの3番目のパラメータはデジタイザペンの筆圧を表していると考えられるが、これまでは透明度属性のglobalAlphaの数値としていた。文字幅にこの数値を掛けた値を置くことに変更している。線描の勢いは良く表現できるが、また、stickerの重なりなどおかしくなってきた。やれやれ、少しずつ進めよう。
backingのレイヤーにstickerの複数のレイヤーを重ねて描画する方法は、重なるclip()の問題を回避できるが、画像のclip()領域外の部分に書かれていた手書き入力(stroke)が見えにくいという問題がある。矩形の画像をそのまま表示してclipしなければこのような問題は存在しないのだが、手書きマシンゆえの手触りの感覚を得るためには避けて通れない道なのだろう。
RIAxDNP Canvasに画像を複数枚重ねて描画するにはなども参考にすればよいのかもしれない。z-indexはcanvasだけのパラメータではなく、ImageオブジェクトのStyleに指定すると画像の描画は早くなるそうだ。clipの問題があるので簡単ではなさそうだけど。
それはともかく、ページ作成の運用上、切り取った画像の周囲近くに手書き文字を書かなければ今のままでも問題は起きない。しかしながら、現実は複雑だ。画像も重ねて配置することもあるから、canvasを別に準備する必要が出てくる。文字だって画像の上だろうが、境界だろうが、どこに書くかわからないのだ。
stickerのstrokeのglobalAlpha(透明度)の値は0.5に設定している。画像ができるだけ明るく表示され、画像上の白いstrokeが十分に判別されて見える設定である。clip外領域のstrokeやページリンクの存在するstrokeのclip領域も実用上はなんとか見える(ここには何か工夫が要りそうだが)。以前は、strokeの第3パラメータの値にglobalAlphaの値を設定していたので、たまたま見えていたが、strokeのwidthの設定を使う筆圧表現に変更したため、明示的に指定する必要が出てきた。日記に掲載している最新のスクリプトは必要に応じて改訂しているのでご注意。
paperMOON on Nexus 7ちょっと見えにくい写真だけど、上の方に「J.J.エイプラムス」への文字とリンクがある。
J.J.エイプラムスのページ。二つの画像をそれぞれ別のcanvasに載せているので、canvasの重ね方の問題が現れている。enchantMOONの描画と異なる形式的に唯一の問題だろうが、現実的に困ることはない。同一のcanvasを使う場合にclip領域の重なりの問題が出たページサンプルである。
データの種類は次の通り。
imageの画像上にstrokeが存在すると、globalAlphaを0.5に設定するので画像が暗くなる。backingの線画のclip領域のstrokeも同様の取り扱いになる。後者の方は他のページへのリンクが存在しているので、線画が暗くなると見難い。リンクの存在をアピールするような明確な分かりやすい表示を考えたほうがよいかもしれない。
曖昧だったプログラム表現が次第に意味づけされ、動作もクリアになり、夾雑物はほとんど取り除かれ、必要なものに置き換えられた。一応完成。
enchantMOON Developers: for all developers of enchantMOON & MOONBlockが公開された。ローカルストレージにぺージへの数値や文字列の保存のシールが公開されている。storage.jsonにデータが格納されることを確認した。今後は、文字認識データが公開されるかどうか、ページデータのアップロード・ダウンロードがいつできるようになるのかが、最大の焦点になるだろう。
ページのアップロードができれば、公開されたページをenchantMOONで共有できるし、外部のツールで作成したデータを取り込んでさらに編集することも可能になる。ダウンロードできれば、ページデータの公開が容易になるし、画像データなどを他のツールで編集することもできるようになる。
今日、famo.usのファウンダーSteve Newcomb氏(blognewcomb - about)から「HTML5 famo.us Keynote & Meetup」のメールが届いた。以前、今年どこかで、famo.us Stress Test Demoを話題にしたと思うが、更新日記に見当たらない。ツイートしたのかな。その時もソースを調べたのだが、表現の凄さはapp.cssとapp.jsに秘められているようにみえて、コードをどのように応用すればよいのかよくわかるところまで読めなかった。いよいよ、オープンソースでフリーで公開されるらしい。
注目したのは、
Welcome to famo.us
a javascript engine and framework that solves HTML5 performance40-60fps on phones, tablets, PCs, and televisions
mod a template, or piece together components
no plug-ins, no WebGL, no Canvas
works on modern browsers
everything is DOM
65KB footprint
すべてはDOM(Document Object Model)だという。プラグインもWebGLもCanvasも不要というところ。注目しておこう。
famo.us Stress Test Demoデザインとエンジニアリングが融合していて、デモは実用的な応用を思いつかせる題材だし、表現としてここまで実現できるものなのかと思わせる。famo.usのデモは第三世代のiPod touchでも軽く動くところが凄いと思うのだが、凄いのはハードウェアなのか、ソフトウェアなのか。最近はハードウェアがソフトウェアを追い越しつつあると実感することが多いのだが、さてどうだろう。
もう一度、codeを調べるか・・・メインのapp.jsで高々72KBしかない。デモHTML全体にリンクされているもの、アイコンや背景画像などすべてを合わせても150KBしかない。.js+.css+.htmのテキストだけなら88KB、「65KB footprint」そのものだ。ページをダウンロードして、cssやjsから読まれるファイルの配置を少し工夫すれば、デスクトップのローカル・サーバーでもそのまま動かせる。要は、今やライブラリの使用法などよりも、HTML5/JavaScript/CSSによるデザインやエンジニアリング、具体的にアプリケーションを作るアイデアや能力が勝負だということを意味しているのではと思う。
デモページのcontainerクラスのmainとoverlayに展開されたHTMLを読むと、結局CSSが描画の基礎となっていることがわかる。CSS Transforms: W3C Working Draft 11 September 2012を調べる必要がありそうだ。JavaScriptだけでは足りないということがはっきりした。CSS追っ掛け再入門が必要かな。どうもいつかは必要になるなと思っていたら、famo.usが導いてくれたね。この領域もまだホットなまま、ワーキングドラフトだ。
現段階ではWorking Draftだけにvendor prefixが付いている場合の取り扱いは、ベンダープレフィックス-CSSの基本に記載がある。famo.usの場合もprefixを取り除いた指定が併記してある。Webコンソールなどでapp.cssを調べると、当然存在していない指定はエラーになる。-webkit-transform: translateZ()と-webkit-transform: matrix3d()関係をメモしておこう。
MOONPhaseが、2.6.0にアップデートした。いつも通り、15分程度で完了。何が新しくなったのかあまりわかっていない。そうこうしていると、enchantMOON開発者用ドキュメントを公開いたしました | enchantMOON News- enchantMOON ; The Hypertext Authoring Tabletが出ましたネタ。
Canvas Context 2D Implementation Status Overviewを見ると、Pathsのclipはサポートされていないことになっている。もう少しstickerのinfo.jsonのデータを調べないと・・・
JsDoc Reference - MOONを実装したハードウェアがenchantMOONになるわけだが、enchantMOONのこれまでとこれから - UEI shi3zの日記を見ると、NO UIであるがゆえに世界初のスケーラブルUX(User eXperience)を主張している。今後他のハードウェアでも動作するように拡張されていくのだろう。現状のclipのストロークデータの使用については、MOON.jsを少し調べたほうが良いのかもしれない。
上記の清水氏の日記には「HyperStickerアプリケーションによるパラダイムシフト」の図と解説が掲載されている。「OS自体がデータの作成と編集機能を提供する」ことになっている。これはMOONPhase OSがハードウェアと結びついた一種のアプリだということを意味する。今のところデータ構造がシンプルだから成立する話だと思う。現実世界のデータはもっと複雑だ。例えば、02/10/2003: [XML] News Industry Text Formatや03/10/2006: [XML] OASIS ODF Adoption Committeeの記事のリンクを辿れば明白である。表現を追求すれば、WordなどのXML出力を見るように調べる気もしないほど複雑になってしまう。
MOONPhase/enchantMOONが僕に示したことは、JSON形式のデータを取り扱うことの便利さとテキストデータにOSのファイル管理とデータ表現の基盤を担わせることができるということだった。データにメタデータによって意味と構造を付与するXML/RDFは取り扱いが難しい。Perlでも適当なライブラリを見つけられなかった(今探せば見つけることができるかもしれないが^^;)。JSONのライブラリはJSONテキストを読み込んでデコードするだけ。簡単に動いた。JSONのデータはPerlのデータ構造に変換される。JSONのデータを構造を見ながら、特定のデータにアクセスするためのPerlのコードを書ける。ハッシュや配列が入り組んだ複雑なデータ構造に簡単にアクセスできるので便利。JSONとは「JavaScript Object Notation」の意。
本日記では、結局、表現の基盤としてHTMLを選択するのが最も効率がよく自由度が高いと考えてきた。データと表現は効率よく分離できないのだ。データとして活用するためにXML/RDFを使おうとしてきたが、活用というレベルには至っていない。日記をAtomやRSSに出力しているに過ぎない。SQLデータベースにAtomデータを取り込むアプリとAjaxブラウザも書いたが、そのままになって発展していない。テキストデータは定型的パターンを持っていれば、テキスト加工が自在だからだ。データがテキストになったことがそのような自由をもたらした。microformat(今はmicrodataかな)の日記への埋め込みにも興味を持った(viewpoint)が、実用的には検索すれば十分という理由から、熱心にはなっていない。我が日記のOSは、Perlと連動するテキストエディタとWebブラウザ/CGI/ローカルサーバーということになる。本日記をMOONPhaseのデータ形式に変換することによって、NO UIでデータを確認しながら意味的な埋め込みができるとおもしろいと思っている。
今のところ、MOONPhase/enchantMOONが取り扱えるのは、カメラによる画像とWebブラウザ表示から切り出したページ画像とデジタルペンによる線画ストロークと文字認識によるテキストだ。今のままでは日記をenchantMOONに取り込めない。
今後、enchantMOONで一番懸念していることは、文字認識の実用性だ。急いだメモは走り書きになる。そのような文字を人間以外が認識できるかという問題があると思っている。文字認識の修正のためにもなんらかのキーボード搭載は必要ではないか。これについては、PC/Macやクラウド上で実現してもよいと思う。storage.jsonに文字認識したテキストが格納されると予想しているのだが、現時点では空だ。
ページデータの入出力がネットワーク上のPC/Macなどとできるようになれば大変おもしろくなると思う。2.6.0では文字の書き味が大変スムースになった。デジタルペンが鉛筆を超えたかもしれない。より走り書きが容易になったので文字認識はさらに難しくなるのではと心配している。
MOONPhaseは年末に向かってアップデートがさらに進むはずだ。その間にstickerデータの解析とデスクトップでの描画を完成させたいものだ。
iOSとAndroidを見ていると、Apple IIとIBM PCを思い出してしまう。Androidマシンは毎日150万台が活性化されているという。Androidは毎日150万台がアクティベートされ2013年末には10億台に到達 - GIGAZINE。新型iPhoneが三日で900万台売れても、多勢に無勢という感じだ(iOS と Android による 世界大戦 -- 2013年/9月 | Agile Cat --- in the cloud)。Appleは今後どう戦うだろう。
enchantMOONは、普通のタブレットとはまったく異なる。データ創造マシンなのだ。一般的なタブレットのようなブラウザだと思っていると、まったく役に立たないだろう。手書きメモマシンとして使うつもりで購入すべきだろう。デジタイザペンの書き味は素晴らしい。次世代のenchantMOONはマシン性能が向上するので、一般的なタブレットの機能を包含することになるだろうけど。
enchantMOONのよいところはデータがその構造毎、仕様が完全に公開されていることだ。他のデバイスでも活用するのが容易だ。様々な可能性を感じている。更新日記といずれ連動させるつもりだが、さて、情報収集デバイスとして活用するには、ページ・データを他のマシンに容易に転送したり、受け取ったりできるとおもしろいと考えている。
新しいiPad/iPad miniが発表されるらしい。それはそれなりに売れることは間違いない。どんな世界が現出することになるのやら。
enchantMOONのこれまでとこれから - UEI shi3zの日記については既に一度言及したが、続けて考えたこと。
enchantMOONのページの見掛けを再現するだけなら、既に見たようにデータをJavaScript/HTML5に変換するだけで、それほど難しいことではない。問題はデータを作るところだからだ。コンパウンド・ドキュメントをUIから生成する部分をエミュレートするとはどういうことになるのだろうと考えた。
デジタイザに書き込んだり、張り付けたつもりのデータを画像ファイルとJSONテキストに自動的に変換しつつ、描画するのが、MOONPhaseである。これに類似したことを行おうとすれば、少なくとも画像処理のソフトウェアが必要になる。思い浮かぶのはGimpだ。これはScript-FuというScheme系のスクリプト言語を持っているが、Perlでも制御できる。汎用的には、GTK+(The Gimp Toolkit Plus)があって、Linux系のGUIの基盤となっている。
問題は入出力にHTML5を使えるかどうかだ。調べると、GTK+ 3.2 Released With HTML5 (Allows Applications To Run In A Web Browser) And Wayland Backends Support ~ Web Upd8: Ubuntu / Linux blogが見つかった。2年前の記事だ。既にWebブラウザで画像処理的なことをする基盤はあるということ。
LibreOfficeのWebブラウザ版もGTK+を使おうとしているらしい。LibreOffice - Wikipediaに「GTK+フレームワークとHTML5のcanvasを利用して、ブラウザ内でソフトウェアを動かしオンライン上で利用することが出来るとしている。2011年10月12日から開催されたLibreOffice Conferenceで開発が発表され、2012年末から2013年の利用開始を目標に開発が行われている。略称としてLOOLが用いられる。」との記載がある。
さて、探してみるかな。そろそろ実用的なものになっているはずだが・・・
「第3のOS」の可能性:Tizenが、メイカームーヴメントを加速させる!? ≪ WIRED.jpネタ。
GTK+やLibreOfficeはHTML5関連では明確な動きを構成していない。HTML5の領域における主導権争いがあるのかな。できる範囲で考えるしかないのだけど・・・
あったね。Help -。TizenはまだCanvasをサポートしていない。これではデバイスは簡単には出ようがない。要は、HTML5 Canvasはホットな状態というか、まだ煮詰まっていないというか、表現としては難しい領域なのだ。これまで見た通り、あらゆるもの、オブジェクトが交差し相互作用する複雑な現実を自在に表現するには足りない部分がある。当然工夫の余地はあると思う。今のところ、PerlでJavaScriptを生成している。JavaScriptそのものの中で簡潔に表現できないかなと思う。できるのかもしれない。ハードウェアが追いついてきたので、ソフトウェア側が進化を迫られているということなのだろう。従来なら書こうともしなかったプログラミング表現が可能となってきた。慌てている感じ。あれ、動くよってわけ。Perlが生成したHTMLは2万行(735KB)にも及ぶのだ。ほとんどはJavaScriptから構成されている。ほぼ瞬時に実行できる。もっとも、もう何年も前からそう思っているわけだけど。もちろん、デスクトップの話で、Webで実用的か試したわけではない。
GNOMEのWaylandはディスプレイ・サーバーという概念に基づいているから、表示そのものに特化しているはずだ。UEI清水氏は編集と表示が分化したところにコンピューティング進化の隘路を見ている。おそらく、PDFのことを言われているのだと思う。編集と表示を同一の場で行うことが、WYSIWYG(What you see is what you get.)の原点であった。Officeアプリケーションはそのものだ。デスクトップであれば、様々なアプリケーションを連携して一つのドキュメントにまとめることができると一応言える。しかしながら、デスクトップのドキュメントは孤立しているように見えるし、あまり書きたいとも思わない。ハイパーテキストの時代だからだし、モバイル時代にはあまりスマートな解ではないということだろうか。
デスクトップにおいてはGoogle Desktopは重要なソフトウェアであったのだが、配布されなくなった。enchantMOON/MOONPhaseはページ数が増えた時の状況に対処できなくてはならない。文字列検索は文字認識の問題になる。ページの分類の問題も出てくる。課題は多い。
さて、Tizenはともかく、Ubuntu(KDE/Qt)をそのまま載せてしまえばというUbuntu for Phoneの動向が気になる。なぜなら、デスクトップがそのまま乗るという話だからだ。いずれ、Mac/Windowsもそうなるわけだ。しかし、結局、そういったことは本質ではない。そもそも創造マシンとは何かということが追求されるべきなのだ。今のところ、enchantMOONだけがそれを追求しようとしている。
創造マシンとしてのenchantMOONenchantMOONからEvernoteに画面イメージを送って、縮小したもの。元のサイズは画面サイズに当然決まる。1024x768ドットでディスプレイ(8インチXGA)のサイズ160x120mmから計算すると、163dpi程度になる。一般的な96dpiのWindowsのデスクトップディスプレイよりは解像度は高い。
シール(HyperSticker)の操作のチップスを書いておくと、シールを画面に移して、シールの回りを指でなぞって操作用のメニュー環を出して活性化させると環の中央付近を指で押しながら動かせるようになる。コンテンツに重ならない問題のない位置にシールを動かしてから、タップしてシールを起動させることができる。
Windows 8.1 proにアップグレードした。リスクはあるので様子を見てからとも思ったが、先延ばしにしても仕方がないので・・・間が悪くて、@nifty auひかりの接続が木曜日から切れていて、昨晩はWiMaxを使ってアップグレード。
まずは一応、TwitterやWebで評判や導入ガイドをチェック。アプリは再インストールになるという話なので、本当にそうなのかな、困った、面倒だなあと思いながら、いずれしなくてはならないからとインストールを開始。
ダウンロードにだいぶ時間を要したが、インストールはほとんど自動。唯一、入力が必要になるのは、Microsoftアカウントになるeメールアドレスを選択して、ログイン・パスワードを入力する時だけ。ここらはいろいろなバリエーションがありそうだ。Windows 8 から Windows 8.1 にアップデートする - Microsoft Windowsを参照のこと。
インターフェースもほとんど変化はない。僕にとっては前のままでよかったし、8.1が特によくなったとも思えないけど。少し、動作が軽くなった感じはあるので、それはよかったかな。結局、自分でインストールしていたものはまったく何もしないでそのまま動いた。インストールの時間は多少掛かるが、ダウンロードを含めて2時間ぐらいかな。まあお気楽なアップグレードだった。WindowsのエディションはWindows 8.1 proとなったが、ロゴはWindows 8のまま。
Windows 8.1についてデスクトップのアップグレードより注目しているのは、次のニュース。Windows 8.1タブレットとその価格だ。
Windows 8.1のタブレットが3万円台の前半で提供される可能性が高まった。この価格ならiOS、AndroidやChromebook対比で優位に立てる。なにしろ、Windowsそのものが動くのだ。何の不足があろう。タブレットの性能がWindowsに追いついてきたのだろう。逆襲が成立する要件が整ってきたように見えてきた。
ハードウェアの高性能化の中で、OSはどのように変化していくのだろう。まずは300ドルWindows 8.1タブレットがどの程度のパフォーマンスを示すのかを確認してみたいね。
デスクトップがモバイルになれば、WiFiダイレクト(Meshネットワーク)などの機能に脚光が当たると思うのだが。
Apple TVでスペシャルイベントを眺めながら、MacBookもOS X Mavericksをインストールしておこうかと開始。もうとっくにイベントの録画も見終わったが、まだダウンロード中。5.29GBもある。Apple Releases Updated OS X Server, iTunes, Xcode, iBooks Author, Configurator, Remote Desktop, And More - Mac Rumorsを見て、iOS 7.0.3も出ているというので、iPad 2でインストールを開始。それほど待つということもなく既に再起動した。iCloudキーチェーンを使うためにパスコードを設定しようという・・・キーチェーンって何。
それはともかく、OS X Mavericksのダウンロードは1GB以上残っている。既に2時間が経過。うーむ、iPadでOS Xを動かすというのも夢というか、無駄なことなのかと思う。iPad 2はiWorkとGarageBandをアップデート中。
Androidの価格破壊は極限まで進む。Android間の競争が主な原因だろう。iOSとAndroidのシェアはkantar.jp/whatsnew/2013/05/07/NewsRelease_130508_ComTech.pdfに詳しい。当然、安価なAndroidのほうがシェアは高いが、iOSより利用頻度が低い、Androidユーザーの謎 ≪ WIRED.jpのように利用頻度はiOSのほうが高い。Androidユーザーは携帯ネットワーク、iOSユーザーはWiFi経由でのアクセスが多いのが特徴。
これで大体最近のモバイルデバイスの動向をほぼ掴まえた感じ。
今朝は13℃、帰宅時は17-20℃の数値を見た。昨日の朝は県北は0℃台だったらしい。BlackBerry and Facebook met to discuss potential buyout, reports WSJ | The Vergeネタ。
BlackBerryはある程度の人気はある。ASCII.jp:BlackBerryの最新フラッグシップモデル「Z30」登場のような製品を出している。BlackBerry Smartphones - Cell Phones - Mobile Phones - Tablets - Apps - USを見ると、フルタッチタイプと従来のキーボードタイプと両方の機種があるし、タブレットのPlayBookがある。独自OSだ。
さて、iOSとAndroidが支配するスマートフォンの世界で第三のOSとして存在を主張できるだろうか。まあ、既に存在するわけだが・・・さて、Amazonはどうするんだっけ。Firefox OS、Ubuntu Phone OSもある。僕の電話機は普通のガラ携だし、今のスマホに足りないものはよくわからない。通信料金を下げてくれっていうぐらいかな。結局ディスプレイが小さいからコンピュータとしてはそれほどの期待は持てない。Facebookにとっては広告塔としての役割があるだろうね。
Ubuntu 13.10「Saucy Salamander(非礼な火トカゲ)」リリース―完全なモバイル対応 OS「14.04」への橋渡し - インターネットコムでは、UbuntuがディスプレイサーバーとしてWaylandではなくMirを選択したことを報じている。Ubuntu 14.04は初めてモバイルを統合したOSとなり、キーコンポーネントがMirということになる。Mir (ディスプレイサーバ) - Wikipediaを参照のこと。
もうモバイルOSは電話のOSというよりは、様々なサイズのディスプレイに対応したOSが使われるということになるのだろうか。ユービキタスOSということかな。
オラクル、手作りタブレット「DukePad」発表!Raspberry Pi内蔵で370ドル | タブクルネタ。手作り品は、安くて性能の高い大量生産品と比べて特徴がないと存在感を示しにくい。が、RaspbianというDebian Linuxが動作するということは・・・
やはり欲しいかな。サーバーを簡単に動かせそうだ。でももう少し安くならないかな・・・
DELL、日本でも『Dell Venue 8 Pro』の製品サポートを公開 | タブクルとの情報。Amazon.com: Dell Venue 8 Pro Tablet (64 GB): Computers & Accessoriesでは、11/1リリースとなっている。
そろそろ、DELL、『Venue 8 Pro』の最新スペック表―32bitOSや10W充電ほか | タブクルで復習しておこう。米アマゾンの価格設定は32GBで299.99ドル。
スペックについては米DELLが8型Windowsタブレット「Venue 8 Pro」の販売開始、価格は300ドル~。 | タブクルも。New Venue 8 Pro + Case + Stylusで、369.99ドル。
これが出るとSurface 2/2 Proは売れる余地が少ないのではと思うし、マイクロソフトはセカンドベンダーの製品の販売の余地を大きく残していると言える。それどころか、タブレットの世界がひっくり返りそうだ。デュアルバンドWiFi+Bluetooth4.0とMiracast搭載。8インチで395g。ほとんど手抜きがないスペックだ。カメラはバックカメラも一応あって5MP、フロントは1.2MP。保護カバーになる「Dell Tablet Wireless Keyboard」と「DELL Active Stylus」ペンにも注目している。解像度は、WXGA 1280 x 800。
Miracastについては、【清水理史の「イニシャルB」】 Windows 8.1の新機能、「テザリング」と「Miracast」を試す - INTERNET Watchが当面の参考になる。