Silverligt 2メモ
久々にSilverlightでもやってみたくてVisual Studio 2008 用 Silverlight Toolsをインストールしようとしたら、Visual Web Developerが必要、みたいなことを言われた。
使ったもの
- Visual Web Developer 2008 EE
- Visual Studio 2008 用 Silverlight Tools Beta 2
開発は楽っぽいけど準備が面倒。Chironを使った方が準備は楽かも。
コード覚書
XAML
全部手書きかよ。
windowとdocument
そういったものはWindows.Browser.HtmlPageに用意されていて、それを使ってスクリプトのやり取りができる。DOMはIEっぽい。
Dim window As Browser.HtmlWindow = Browser.HtmlPage.Window Dim document As Browser.HtmlDocument = Browser.HtmlPage.Document Dim input As Browser.HtmlElement = document.CreateElement("input") document.Body.AppendChild(input) input.AttachEvent("onkeyup", AddressOf keyevent)
プロパティとメソッド
プロパティの取得, 設定にはGetProperty, SetPropertyを使う。Silverlightのオブジェクトも設定できるので、ブラウザ側からもコントロールできる、と思う。
Dim textbox As New TextBox window.SetProperty("aaa", textbox)
ブラウザ側のメソッドを呼び出すときはinvokeを使う。
' Firebugのconsole.logを呼び出す Dim console As Browser.ScriptObject = window.GetProperty("console") console.Invoke("log", 1)
ビルド
SilverlightApplication1で作ったら、他のVisual Studioと同じように
\My Documents\Visual Studio 2008\Projects\SilverlightApplication1\SilverlightApplication1\Bin\Release
に出力される。使わなそうな言語関係のファイルとかも勝手に。
感想
VWD2008EEでコントロール作るのはちょっと面倒くさそう。.NETの一部を借りたくなったときに使ったりするのには便利かも。
JSActionsでGPSから座標を取得してGoogleMapsに反映
Tombloo + JSActionsでWSHを実行するコードは下ページからのコピペです。凄い簡単にFirefoxからWSHが使える。
http://d.hatena.ne.jp/brazil/20090102/1230876349
GPSからデータを取得する
GPSからデータを取得するのに自作コンポーネントを使ってますが、MSCommを使ったりとか、そもそもWSHを使わないとか、色々方法はありますんでその辺は適当に。
自作コンポーネントはこんなの: http://d.hatena.ne.jp/arikui/20071212/1197453931
GoogleMapsを用意する
window.map = new GMap2(document.getElementById("map"));
みたいに、外からGoogleMapsのAPIが使えるやつ。
コード
JSActionsのコードからwindow.mapを参照できなかったので、javascriptスキームで座標を指定してます。
var T = Components.classes['@brasil.to/tombloo-service;1'].getService().wrappedJSObject; (function(times){ var data = ""; var count = 1; var timer = setInterval(function(){ data += getGpsData(); var coord = getCoordinates(); if(coord) window.location.href = "javascript:window.map.setCenter(new GLatLng("+coord+"));void(0);" if(++count > times) clearInterval(timer); }, 1000); function getCoordinates(){ var _data = data.split("\n"); var match = null; while(_data.length){ if(match = /\$GPGGA.{12}(.{9}).{3}(.{10})/.exec(_data.pop())){ data = _data.join("\n"); var coord = [ parseInt(match[1].substr(0, 2), 10) + match[1].substr(2) / 60, parseInt(match[2].substr(0, 3), 10) + match[2].substr(3) / 60, ]; if(coord[0] && coord[1]) return coord; else return null; } } return null; } })(5); function getGpsData(){ return executeWSH(function(){ var gps = WSH.CreateObject("CommPort.Comm"); gps.port = 23; gps.BaudRate = 4800; gps.Open(); var s = gps.Input(); WSH.Sleep(1000); s += gps.Input(); gps.Close(); return s; }); } // http://d.hatena.ne.jp/brazil/20090102/1230876349 function executeWSH(func, args){ args = args || []; with(T){ var bat = getTempFile('bat'); var script = getTempFile(); var out = new LocalFile(script.path + '.out'); putContents(bat, [ 'cscript //E:JScript //Nologo', script.path.quote(), '>', out.path.quote()].join(' ')); putContents(script, args.map(function(a, i){return 'var ARG_' + i + ' = ' + uneval(a) + ';'}).join('\n') + 'WScript.echo(' + func.toSource() + '(' + args.map(function(a, i){return 'ARG_' + i}).join(',') + '));'); new Process(bat).run(true, [], 0); var res = getContents(out).replace(/\s+$/, ''); bat.remove(false); script.remove(false); out.remove(false); return res; } }
ウィンドウをキャプチャしてAVI動画を作る (.NET)
機能のつづき: ウィンドウをキャプチャしてアニメーションGIFを作る (.NET) - f8g
AVIファイルの生成
WindowsではDirectShowを使ってAVIファイルを作ることができます。だた、DirectShowを.NETで使おうとすると結構面倒なので、これもまたその辺のライブラリを使って簡単にやります。
http://www.codeproject.com/KB/audio-video/avifilewrapper.aspx
昨日みたいに適当にコンパイルして参照しちゃいましょう。
' 下記以外は前と一緒 Dim AviManager As AviFile.AviManager Dim AviStream As AviFile.VideoStream Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick Dim bmp As Bitmap = Me.GetBitmapByHwnd(Me.GetHwndByTitle("メモ帳")) ' 最初のフレームか If Me.AviStream Is Nothing Then Timer1.Enabled = False ' 一回止めないとヤバイ Me.AviStream = Me.AviManager.AddVideoStream(True, 1.0! / (Timer1.Interval / 1000.0!), bmp) Timer1.Enabled = True Else Me.AviStream.AddFrame(bmp) End If bmp.Dispose() End Sub Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click If SaveFileDialog1.ShowDialog() = Windows.Forms.DialogResult.OK Then Me.AviManager = New AviFile.AviManager(SaveFileDialog1.FileName, False) Timer1.Enabled = True End If End Sub Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click If Timer1.Enabled Then Timer1.Enabled = False Me.AviManager.Close() End If End Sub
最初のフレームのとき止めないとヤバイのは、
みたいなダイアログが出てきてから動画を作るのに、バックグラウンドでTimerが動いてて、変なことになるからです。多分。
今回利用したライブラリは動画を作るだけじゃなく音声も付けられるようなので、自作ラップと一緒に動画にして楽しむ、といった使い方もできます。
ウィンドウをキャプチャしてアニメーションGIFを作る (.NET)
.NET Framework v3.5で動作確認。(v2.0.50727でも動くと思う)
使ったもの
- Windows
- Visual Basic 2008 express edition
- 時間
ウィンドウのキャプチャ
' Imports System.Runtime.InteropServices ''' <summary> ''' GetWindowRectで使う構造体 ''' </summary> <StructLayout(LayoutKind.Sequential)> _ Structure RECT Public left As Integer Public top As Integer Public right As Integer Public bottom As Integer End Structure ''' <summary> ''' 画像を別のコンテキストに送る ''' 参考: http://yokohama.cool.ne.jp/chokuto/urawaza/api/BitBlt.html ''' </summary> <DllImport("gdi32.dll")> _ Private Shared Function BitBlt(ByVal hDestDC As IntPtr, _ ByVal x As Integer, _ ByVal y As Integer, _ ByVal nWidth As Integer, _ ByVal nHeight As Integer, _ ByVal hSrcDC As IntPtr, _ ByVal xSrc As Integer, _ ByVal ySrc As Integer, _ ByVal dwRop As Integer) As Integer End Function ''' <summary> ''' ウィンドウを最前面に出す ''' </summary> <DllImport("user32.dll")> _ Private Shared Function SetForegroundWindow(ByVal hWnd As IntPtr) As Boolean End Function ''' <summary> ''' ウィンドウの座標を取得 ''' </summary> <DllImport("user32.dll")> _ Private Shared Function GetWindowRect(ByVal hwnd As IntPtr, ByRef rect As RECT) As Boolean End Function ''' <summary> ''' ウィンドウの座標を取得 ''' </summary> Private Shared Function GetWindowRect(ByVal hwnd As IntPtr) As RECT Dim rec As RECT GetWindowRect(hwnd, rec) Return rec End Function ''' <summary> ''' ウィンドウのハンドルをタイトル(正規表現)から取得 ''' </summary> Private Function GetHwndByTitle(ByVal title As String) As IntPtr Dim hwnd As IntPtr = Nothing Dim processes() As Process = Diagnostics.Process.GetProcesses() For Each p In processes If System.Text.RegularExpressions.Regex.IsMatch(p.MainWindowTitle, title) Then hwnd = p.MainWindowHandle Exit For End If Next Return hwnd End Function ''' <summary> ''' ウィンドウの画像を取得 ''' </summary> Private Function GetBitmapByHwnd(ByVal hwnd As IntPtr) As Bitmap ' ウィンドウが後ろに隠れてるとちゃんとキャプチャできませんので SetForegroundWindow(hwnd) System.Threading.Thread.Sleep(100) Dim rect As RECT = GetWindowRect(hwnd) ' GetWindowRectのサイズとGraphics.FromHwndのサイズが違うので適当に調節 Dim bmp As New Bitmap(rect.right - rect.left - 8, rect.bottom - rect.top - 50) Dim image As Graphics = Graphics.FromImage(bmp) Dim window As Graphics = Graphics.FromHwnd(hwnd) Dim imageHdc As IntPtr = image.GetHdc Dim windowHdc As IntPtr = window.GetHdc BitBlt(imageHdc, 0, 0, bmp.Width, bmp.Height, windowHdc, 0, 0, 13369376) ' SRCCOPY: 13369376 image.ReleaseHdc(imageHdc) window.ReleaseHdc(windowHdc) Return bmp End Function
アニメーションGIFの生成
System.Drawing.BitmapにはSaveAddというメソッドがありますが騙されてはいけません。これで作れるのはマルチフレームなTIFFだけです。GDI+ではアニメーションGIFが作れないらしいので、バイナリに直接書き込んでいくしかなさそうです。今更GIFの仕様とか理解するのは面倒なので、その辺に転がってるライブラリを使います。
http://www.codeproject.com/KB/GDI-plus/NGif.aspx
ログインしてダウンロードしたら適当にコンパイル。
> c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\csc /target:library /out:NGif.dll /warn:0 /nologo /debug *.cs
Dim GifEncoder As New Gif.Components.AnimatedGifEncoder Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Timer1.Interval = 500 End Sub Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick Dim bmp As Bitmap = Me.GetBitmapByHwnd(Me.GetHwndByTitle("メモ帳")) Me.GifEncoder.AddFrame(bmp) bmp.Dispose() End Sub ''' <summary> ''' キャプチャ開始 ''' </summary> Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click If SaveFileDialog1.ShowDialog() = Windows.Forms.DialogResult.OK Then Me.GifEncoder.Start(SaveFileDialog1.FileName) Me.GifEncoder.SetFrameRate(1.0! / (Timer1.Interval / 1000.0!)) Me.GifEncoder.SetRepeat(0) Timer1.Enabled = True End If End Sub ''' <summary> ''' キャプチャ終了 ''' </summary> Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click If Timer1.Enabled Then Timer1.Enabled = False Me.GifEncoder.Finish() End If End Sub
次回はAVIバージョン。
netkeibaの出馬表に過去の競争成績を表示するGreasemonkey(修正)
http://d.hatena.ne.jp/arikui/20060513/1147528160
久々に動かしたら動かなくなってたので修正。多少軽くもなったか。
pastRaceNumを変えると、過去何走表示するかを変更できます。
画像を分割してランダムに配置するモザイク
カラー画像だと少々面倒臭そうなので、グレースケールに変換してからやります。手順はこんな感じです。
- グレースケール化
- 10 * 10 に分割
- 分割画像のRGBを平均して分類
- 分類ごとにランダムソート
- 再配置
コード
ImageProcessing.prototype.f= function(fnc){ return fnc(this); }; var clips = {}; ip = ImageProcessing .load("image.png") .lock() // グレースケール .each(function(px, x, y, self){ self.setPixel(x, y, px.grayScale()); }) .update() // 分割して色ごとにまとめる // 分割した画像の平均でまとめるてるので結構適当 .f(function(self){ var clip, color; for(var x = 0; x > self.canvas.width - 10; x += 10){ for(var y = 0; y < self.canvas.height - 10; y += 10){ clip = self.clip(x, y, 10, 10); color = Math.round(clip.average().r); if(clips[color]) clips[color].push(clip); else clips[color] = [clip]; } } return self; }) // 分割した画像をごちゃ混ぜにして再配置 .f(function(self){ var clone = ip.clone().clear(); // 別のCanvasに書き込む for(var n in clips) (function(parts, n){ var positions = parts.map(function(clip){ clip.toString = function(){ return Math.random(); }; return [clip.origin.x, clip.origin.y]; }); parts.sort(); parts.forEach(function(clip, i){ clone.merge(clip, positions[i][0], positions[i][1]); }); })(clips[n], n); return clone; }) .update();
問題点
この方法だと、分割画像の平均値がユニークなとき、その画像は結局同じ場所に配置されてしまいます。RGBで分類する場合であれば、なおさらその傾向が出てしまいます。
より細かく分割するとか、分類するときに閾値を持たせてやれば、ある程度回避できそうです。
感想
最近こんなことばっかりやってて飽きてきました。
Tombloo Mozaic でソート by 色
Greasemonkeyでやろうとしたけど動かなかったんで、そのままHTMLにコード書き込みました。HTMLは通常、
Firefoxのプロファイルディレクトリ\extensions\tombloo@brasil.to\chrome\content\library\Mosaic.html
にあります(多分)。Mosaic.htmlの一番下の方にでもコードを書いてやります。
<script src="http://github.com/arikui/image_processing.js/tree/master%2Fimage_processing.js?raw=true"></script> <script type="text/javascript"> (function(){ // sort button var button = $("control").appendChild(document.createElement("button")); button.innerHTML = "sort"; var photos = []; button.onclick = function(){ this.disabled = true; var i = 0; // 画像から色を取って配列に入れてる deferredForEach($("mosaic").getElementsByTagName("img"), function(photo){ document.title = i++ / 10 + "%"; // 進捗状況が一目で分かる! // 画像がない時は何もしません if(!photo.naturalWidth) return wait(0); photos.push({ image : photo, color : ImageProcessing.load(photo.src).average(), toString: function(){ return ("00" + Math.round(this.color.average()).toString()).slice(-3); } }); return wait(0); }) // 配列に入れたやつをソートして戻してる .addCallback(function(){ document.title = "complete"; photos.sort(); $("mosaic").innerHTML = ""; photos.forEach(function(photo){ $("mosaic").appendChild(photo.image); }); }) .callback(); }; })() </script>
sortボタンが現れます。
押して長時間するとソートされます。
応用
画像からモザイク画が作れそうですね。