最近の更新

関連


その他いろいろ

多機能フィードリーダSynapse製作中

2月までは目が回るほど忙しい

MODxでつくる! 最強のCMSサイト カバー
MODxでつくる! 最強のCMSサイト

PukiWikiをで外部のサイトへのリンクを踏むと、リファラが送信されてしまいます。これを防ぐパッチがありますが、内部・外部でWiki側のURLが複数存在する場合に対応するコードを追加し、外部へのリンクのみ緩衝用のページ(googleやhatena, ime.nu等)を介するパッチを書きました。

diff -ur a/lib/make_link.php b/lib/make_link.php
--- a/lib/make_link.php	2006-10-22 05:40:25.000000000 +0900
+++ b/lib/make_link.php	2008-11-22 16:07:46.000000000 +0900
@@ -385,7 +385,20 @@
 		} else {
 			$rel = ' rel="nofollow"';
 		}
-		return '<a href="' . $this->name . '"' . $rel . '>' . $this->alias . '</a>';
+    //! ----
+    //! jump via redirector for external pages
+    global $do_redirect_externals, $redirect_url, $script_urls;
+    if ($do_redirect_externals) {
+      $ru = $redirect_url;
+      foreach ($script_urls as $s)
+        if (strncasecmp($this->name, $s, strlen($s)) == 0) {
+          $ru = '';
+          break;
+        }
+    } else
+      $ru = '';
+    //! ----
+		return '<a href="' . $ru . $this->name . '"' . $rel . '>' . $this->alias . '</a>';
 	}
 }

diff -ur a/pukiwiki.ini.php b/pukiwiki.ini.php
--- a/pukiwiki.ini.php	2006-10-22 05:40:24.000000000 +0900
+++ b/pukiwiki.ini.php	2008-11-22 11:28:52.000000000 +0900
@@ -116,6 +116,16 @@
 // Specify PukiWiki URL (default: auto)
 //$script = 'http://example.com/pukiwiki/';

+// Redirect URLs
+$do_redirect_externals = true;
+$redirect_url = 'リダイレクト用のURL';
+$script_urls = array(
+    'http://192.168.1.100/',
+    'http://example.com/wiki/',
+    './',
+    '/',
+    );
+
 // Shorten $script: Cut its file name (default: not cut)
 //$script_directory_index = 'index.php';

PukiWiki 1.4.7_nofb_utf8対象です。

リダイレクトしてくれるのは

  • http://a.hatena.ne.jp/go?
  • http://ime.nu/
  • http://www.google.com/url?sa=D&q=

など。公に提供しているものではないと思うので、真面目にやるなら自分で用意した方が良いでしょう。

PukiWikiでは[[タイトル:http://link.example.com/a/b/c]]のようにしてリンクを張ることが出来ますが、同じホスト内のWiki以外の箇所にリンク先したい場合があります。

特にエイリアスやヴァーチャルホストなどで、ホスト部分が変動する場合には通常の方法では対応できません。そこで同ホストの相対・絶対URLにリンクを張るパッチを書いてみました。

下記のパッチをPukiWiki-1.4.7_notb_utf8に適用することで、

  • [[タイトル:./d/e/f]]のようにしてhttp://link.example.com/wiki/d/e/fにリンク (相対URL。PukiWikiがhttp://link.example.com/wiki/にある場合)
  • [[タイトル:/d/e/f]]のようにしてhttp://link.example.com/d/e/fにリンク (絶対URL)

と、相対・絶対URLにリンクを張ることが出来るようになります。

[~/pukiwiki-1.4.7_notb_utf8]$ patch -p1 < thispatch

diff -ur a/lib/make_link.php b/lib/make_link.php
--- a/lib/make_link.php	2006-10-22 05:40:25.000000000 +0900
+++ b/lib/make_link.php	2008-11-22 11:30:12.000000000 +0900
@@ -360,7 +360,7 @@
  (?:>|:)
 )?
 (                 # (3) url
- (?:(?:https?|ftp|news):\/\/|mailto:)[\w\/\@\$()!?&%#:;.,~'=*+-]+
+ (?:(?:https?|ftp|news):\/\/|mailto:|\.\/|(?<!\w)\/)[\w\/\@\$()!?&%#:;.,~'=*+-]+
 )
 (?($s1)\]\])      # close bracket
 EOD;



20081122-2307 絶対URLの適用範囲が広すぎたので修正

クリップボードを監視し、コピーされた語句で検索・バルーン表示するClipDic v1.00をBSD Licenseのもと公開しました。

標準でGoogle検索、Wikipedia検索が含まれていますが、簡単な.NETアセンブリを書くことでユーザが検索モジュールを追加することが出来ます。(辞書を引くモジュールなどを作ると便利)

.NET Framework 3.5以上が必要です。

2008 10 13

Synapse 0.0.17α

一年と数ヶ月ぶりにフィードリーダSynapse 0.0.17αをリリースしました。

  • フィード巡回時の重さを軽減
  • idがintだったためしばらく使うとオーバーフローする問題を解消(ulongに変更)
  • 「ブラウザで開く」(O)と対にして使うグローバルホットキー「Synapseをアクティブ化する」(Ctrl+Alt+O)
  • 未読件数が時々狂うのは未だ直ってない

これだけ時間を空けると自分の書いたソースでも訳が分からない。一度リファクタリングしないとこのまま開発するのは難しそう。

あふ的操作感+Python拡張可能な2画面ファイラ、内骨格
config.pyを弄ることでキー割り当てなどのきめ細かい設定が可能ですが、ファイラというアプリの特性上、ローカルな環境に応じた設定(ジャンプ先リストなど)が多くなります。

複数の環境で内骨格を使う場合、共通部分とローカルな部分に分けられたら便利だと思い、extentionsに置いたlocal_config.pyをconfig.pyの後に読み込むデコレータを書いてみました。

config.pyのdef configure前に以下のコードを書き、


# ローカル設定を読み込むデコレータ
def localize(f):
    def localized_configure(window):
        f(window)
        try:
            import local_config
            reload(local_config)
            eval("local_config.%s(window)" % f.__name__)
        except ImportError:
            print "no local configure file."
    return localized_configure

configure(_*)にデコレータを適用します。


# 設定処理
@localize ←これ
def configure(window):
       (略)

extentions/local_configure.pyに普通のconfig.pyと同様にconfigure(_*)を書けば、
config.pyに書かれたconfigure(_*)が呼び出された後にlocal_configure.py上の対応する関数が呼ばれます。

もしかしたらもっと上手い方法があるかもしれませんが、一応これでも不便無く使えています。

PythonのTwitter APIラッパー、python-twitterを使ってみました。

% sudo easy_install python-twitter

import twitter
api = twitter.Api()
statuses = api.GetUserTimeline('user_name')
print 'statuses'
print statuses
print '-'*20
for s in statuses:
        print 'status:'
        print s.text
return statuses

発言は取れますが、twitter.Statusにin_reply_to_user_id, in_reply_to_status_idが無いので、会話の流れがよくわからない。

ということでパッチを書きました。


--- twitter.py  2007-07-16 02:38:54.000000000 +0900
+++ twitter.py     2008-09-11 13:50:12.000000000 +0900
@@ -41,7 +41,9 @@
                id=None,
                text=None,
                user=None,
-               now=None):
+               now=None,
+               in_reply_to_user_id=None,
+               in_reply_to_status_id=None):
     '''An object to hold a Twitter status message.

     This class is normally instantiated by the twitter.Api class and
@@ -66,6 +68,8 @@
     self.text = text
     self.user = user
     self.now = now
+    self.in_reply_to_user_id = in_reply_to_user_id
+    self.in_reply_to_status_id = in_reply_to_status_id

   def GetCreatedAt(self):
     '''Get the time this status message was posted.
@@ -281,7 +285,9 @@
     return Status(created_at=data.get('created_at', None),
                   id=data.get('id', None),
                   text=data.get('text', None),
-                  user=user)
+                  user=user,
+                  in_reply_to_user_id=data.get('in_reply_to_user_id', None),
+                  in_reply_to_status_id=data.get('in_reply_to_status_id', None))

 class User(object):

これで返信先も表示できます。


import twitter
def run():
        api = twitter.Api()
        statuses = api.GetUserTimeline('user_name')
        for s in statuses:
                print 'status:'
                if s.in_reply_to_status_id:
                        status = api.GetStatus(s.in_reply_to_status_id)
                        print '> (%s) ' % status.user.screen_name, status.text
                print s.text
        return statuses

if __name__=='__main__':
        run()

引き続きSIMDの練習。

モノクロやネガのようにリニアに読み書きすればいい処理と違って、y方向近傍を読まざるを得ない座標変換が入ると、キャッシュミスによる遅延が効いてきそうですが、避けて通るわけにも行かないので挑戦してみます。
まずは画像のスケール変換を書いてみました。

static wxImage ResizeImage(wxImage &src_image, double size) {
 int src_real_w = src_image.GetWidth();
 int src_real_h = src_image.GetHeight();
 int dst_real_w = src_real_w*size;
 int dst_real_h = src_real_h*size;
 // 実際のビットマップの大きさ
 int dst_w = std::min<int>(src_real_w, src_real_w*size);
 int dst_h = std::min<int>(src_real_h, src_real_h*size);
 int src_w = dst_w*src_real_w/dst_real_w;
 int src_h = dst_h*src_real_h/dst_real_h;
 
 if (dst_w*dst_h<=4)
  return wxImage();

 unsigned char* src = src_image.GetData();
 unsigned char* src4 = (unsigned char*)malloc(src_w*src_h*3*4);

 int aligned_dst_size = dst_w*dst_h*3;
 aligned_dst_size += 3-(aligned_dst_size+3)%4;
 unsigned char* dst = (unsigned char*)malloc(aligned_dst_size);
 unsigned char* dst4 = (unsigned char*)malloc(dst_w*dst_h*3*4);
 // byte => DWORD
 for (int v=0; v<src_h; v++)
  for (int u=0; u<src_w; u++) {
   int p_s = (v*src_real_w + u)*3;
   int p_s4 = (v*src_w + u)*3*4;
   src4[p_s4 + 0] = src[p_s + 0];
   src4[p_s4 + 4] = src[p_s + 1];
   src4[p_s4 + 8] = src[p_s + 2];
  }
 switch (0) {
  case 0: {
   // bilinear
   // 参照先srcを越えない範囲
   int v_to = std::min<int>(dst_h-1, (src_h-1)*size);
   __m128 xmm_dummy;
   float dummy = 0;
   /* 水平用 */
   xmm_dummy = _mm_load1_ps(&dummy);
   // RGB一括ロードのマスク
   __m128i xmm_mask;
   xmm_mask.m128i_i32[0] = 0x000000ff;
   xmm_mask = _mm_shuffle_epi32(xmm_mask, 0);
   xmm_mask.m128i_i32[3] = 0;
   
   char *buf = (char*)_mm_malloc(16, sizeof(__m128i));
   for (int v=1; v<v_to; v++) {
     long l = v/size;
     long l1 = l+1;
     double wv = v/size - l;
     double wv1 = 1 - wv;
     // プリフェッチ
     _mm_prefetch((const char*)(src4 + l*src_w*3*4), _MM_HINT_T2);
     _mm_prefetch((const char*)(src4 + l1*src_w*3*4), _MM_HINT_T2);

     // 参照先srcを越えない範囲
     int u_to = std::min<int>(dst_w-1, (src_w-1)*size);
     for (int u=1; u<u_to; u++) {
      long k = u/size;
      long k1 = k+1;

      double wu = u/size - k;
      double wu1 = 1 - wu;
      long p_s_0_0 = (l*src_w + k)*3*4; /* (k, l) */
      long p_s_0_1 = (l*src_w + k1)*3*4; /* (k+1, l) */
      long p_s_1_0 = (l1*src_w + k)*3*4; /* (k, l+1) */
      long p_s_1_1 = (l1*src_w + k1)*3*4; /* (k+1, l+1) */
      long p_d = (v*dst_w+u)*3*4;
      /*
      dst4[p_d + 0] = (src4[p_s_0_0 + 0]*wu1 + src4[p_s_0_1 + 0]*wu)*wv1
       + (src4[p_s_1_0 + 0]*wu1 + src4[p_s_1_1 + 0]*wu)*wv;

      dst4[p_d + 4] = (src4[p_s_0_0 + 4]*wu1 + src4[p_s_0_1 + 4]*wu)*wv1
       + (src4[p_s_1_0 + 4]*wu1 + src4[p_s_1_1 + 4]*wu)*wv;

      dst4[p_d + 8] = (src4[p_s_0_0 + 8]*wu1 + src4[p_s_0_1 + 8]*wu)*wv1
       + (src4[p_s_1_0 + 8]*wu1 + src4[p_s_1_1 + 8]*wu)*wv;
      */
      __m128 xmm_dst, xmm_wu, xmm_wv;

      // DWORD転送 xmm_src_all_d: 0 [b] [g] [r]
      __m128i xmm_all_tmp;
      __m128 xmm_src_all_0, xmm_src_all_1, xmm_src_all_2, xmm_src_all_3;
      xmm_all_tmp = _mm_loadu_si128((__m128i *)(src4 + p_s_0_0));
      xmm_all_tmp = _mm_and_si128(xmm_mask, xmm_all_tmp);
      xmm_src_all_0 = _mm_cvtepi32_ps(xmm_all_tmp);
      xmm_all_tmp = _mm_loadu_si128((__m128i *)(src4 + p_s_0_1));
      xmm_all_tmp = _mm_and_si128(xmm_mask, xmm_all_tmp);
      xmm_src_all_1 = _mm_cvtepi32_ps(xmm_all_tmp);
      xmm_all_tmp = _mm_loadu_si128((__m128i *)(src4 + p_s_1_0));
      xmm_all_tmp = _mm_and_si128(xmm_mask, xmm_all_tmp);
      xmm_src_all_2 = _mm_cvtepi32_ps(xmm_all_tmp);
      xmm_all_tmp = _mm_loadu_si128((__m128i *)(src4 + p_s_1_1));
      xmm_all_tmp = _mm_and_si128(xmm_mask, xmm_all_tmp);
      xmm_src_all_3 = _mm_cvtepi32_ps(xmm_all_tmp);
      
      // 色ごとに集める xmm_rtmp: r3 r2 r1 r0
      __m128 xmm_colors_tmp[3]; // r, g, b
      xmm_colors_tmp[0] = _mm_shuffle_ps(xmm_src_all_0, xmm_src_all_0, SHUFFLEBITS(3, 3, 3, 0)); // 0 0 0 r0
      xmm_colors_tmp[0] = _mm_or_ps(xmm_colors_tmp[0], _mm_shuffle_ps(xmm_src_all_1, xmm_src_all_1, SHUFFLEBITS(3, 3, 0, 3))); // 0 0 r1 0
      xmm_colors_tmp[0] = _mm_or_ps(xmm_colors_tmp[0], _mm_shuffle_ps(xmm_src_all_2, xmm_src_all_2, SHUFFLEBITS(3, 0, 3, 3))); // 0 r2 0 0
      xmm_colors_tmp[0] = _mm_or_ps(xmm_colors_tmp[0], _mm_shuffle_ps(xmm_src_all_3, xmm_src_all_3, SHUFFLEBITS(0, 3, 3, 3))); // r3 0 0 0
      xmm_colors_tmp[1] = _mm_shuffle_ps(xmm_src_all_0, xmm_src_all_0, SHUFFLEBITS(3, 3, 3, 1));
      xmm_colors_tmp[1] = _mm_or_ps(xmm_colors_tmp[1], _mm_shuffle_ps(xmm_src_all_1, xmm_src_all_1, SHUFFLEBITS(3, 3, 1, 3)));
      xmm_colors_tmp[1] = _mm_or_ps(xmm_colors_tmp[1], _mm_shuffle_ps(xmm_src_all_2, xmm_src_all_2, SHUFFLEBITS(3, 1, 3, 3)));
      xmm_colors_tmp[1] = _mm_or_ps(xmm_colors_tmp[1], _mm_shuffle_ps(xmm_src_all_3, xmm_src_all_3, SHUFFLEBITS(1, 3, 3, 3)));
      xmm_colors_tmp[2] = _mm_shuffle_ps(xmm_src_all_0, xmm_src_all_0, SHUFFLEBITS(3, 3, 3, 2));
      xmm_colors_tmp[2] = _mm_or_ps(xmm_colors_tmp[2], _mm_shuffle_ps(xmm_src_all_1, xmm_src_all_1, SHUFFLEBITS(3, 3, 2, 3)));
      xmm_colors_tmp[2] = _mm_or_ps(xmm_colors_tmp[2], _mm_shuffle_ps(xmm_src_all_2, xmm_src_all_2, SHUFFLEBITS(3, 2, 3, 3)));
      xmm_colors_tmp[2] = _mm_or_ps(xmm_colors_tmp[2], _mm_shuffle_ps(xmm_src_all_3, xmm_src_all_3, SHUFFLEBITS(2, 3, 3, 3)));

      /* wu1 wu wu1 wu */
      xmm_wu.m128_f32[0] = wu1;
      xmm_wu.m128_f32[1] = wu;
      xmm_wu = _mm_shuffle_ps(xmm_wu, xmm_wu, SHUFFLEBITS(1, 0, 1, 0));
      /* wv1 wv1 wv wv */
      xmm_wv.m128_f32[0] = wv1;
      xmm_wv.m128_f32[2] = wv;
      xmm_wv = _mm_shuffle_ps(xmm_wv, xmm_wv, SHUFFLEBITS(2, 2, 0, 0));

#define BILINEAR_LINE_N(COLORINDEX) { \
      /* 積 */ \
      xmm_dst = _mm_mul_ps(xmm_colors_tmp[COLORINDEX], xmm_wu); \
      xmm_dst = _mm_mul_ps(xmm_dst, xmm_wv); \
      /* 水平 */  \
      xmm_dst = _mm_hadd_ps(xmm_dst, xmm_dummy); \
      xmm_dst = _mm_hadd_ps(xmm_dst, xmm_dummy); \
      /* 結果を非テンポラルに書き込む */ \
      xmm_all_tmp = _mm_cvtps_epi32(xmm_dst); \
      _mm_stream_si128((__m128i*)(buf), xmm_all_tmp); \
      dst4[p_d + COLORINDEX*4] = buf[0]; \
      }
      BILINEAR_LINE_N(0);
      BILINEAR_LINE_N(1);
      BILINEAR_LINE_N(2);
#undef BILINEAR_LINE_N
     }
   }
   _mm_free(buf);
  } break;
 }

 // DWORD => byte
 for (int v=0; v<dst_h; v++)
  for (int u=0; u<dst_w; u++) {
   int p_d = (v*dst_w + u)*3;
   int p_d4 = p_d*4;
   dst[p_d + 0] = dst4[p_d4 + 0];
   dst[p_d + 1] = dst4[p_d4 + 4];
   dst[p_d + 2] = dst4[p_d4 + 8];
  }
 
 free((void*)src4);
 free((void*)dst4);

 wxImage result = wxImage(dst_w, dst_h);
 result.SetData(dst);
 return result;
}

とりあえず動くことは動く。絶望的に遅いので特に評価はしていませんが…
プリフェッチ命令とか非テンポラルストアとか、意味があるのか分からないものの使ってみました。
命令とコストの対応が分かってないので、何をやるにしても方針が立ちません。やはり一度真面目に勉強するべきか。

追記: float(ps)は遅そうなので、ふつうのCでSSE使わずに、256倍して整数で書いてみました。結果は2倍以上高速に…
やはり命令の使い方の前にコストを知る必要がありそうです。

2008 07 28

SIMD (MMX)

カラーホイールを回すとCPUに負荷が掛かるようなので、彩度・輝度空間の描画部分にSIMD命令を使って高速化を図ってみました。
MMXとかSSE2を使うのにはインラインアセンブラしかないと思いこんでいたが、

#include <emmintrin.h>
#include <xmmintrin.h>
#include <mmintrin.h> 

これらのヘッダファイルをインクルードすることで、イントリンシック記法が使えるそうです。(参考:SIMD命令セットの記述方法)

高速化する前のコード

for (unsigned long i=0; i<svw; i++)
	for (unsigned long j=0; j<svw; j++) {
		unsigned long s = (i*0xff)/svw;
		unsigned long v = (j*0xff)/svw;
		unsigned long vff = v*0xff;
		unsigned long vffff = vff*0xff;
		unsigned long vs = v*s;
		unsigned long vsff = v*s*0xff;
		unsigned long fvs = f*vs;
		unsigned long P = (vff-vs)>>8; // v*(0xff-s)
		unsigned long q = (vffff-fvs)>>16; // v*(0xff-f*s)
		unsigned long t = (vffff-vsff+fvs)>>16; // v*(0xff-s+f*s)
		unsigned long rgbmap[6][3] = {
			{v, t, P},
			{q, v, P},
			{P, v, t},
			{P, q, v},
			{t, P, v},
			{v, P, q},
		};
		unsigned long svpos = (svw2-j*svw-svw+i)*3;
		sv_data[svpos] = rgbmap[modh][0];
		sv_data[svpos + 1] = rgbmap[modh][1];
		sv_data[svpos + 2] = rgbmap[modh][2];
	}

XMMレジスタにおいてワード整数のMMX命令セット PMULLW, PSUBW, PSRLWを使って高速化したコード

#define COLORWHEEL(RR, GG, BB) {\
	__m128i m_f; \
	m_f.m128i_i32[0] = 0;		m_f.m128i_i32[1] = 256;		m_f.m128i_i32[2] = f;		m_f.m128i_i32[3] = f1; \
	for (unsigned long c=0,p=0; c<svw2-1; c++,p+=3) { \
		unsigned long i = c%svw; \
		unsigned long j = svw-1-c/svw; \
		__m128i  m_a, m_b, m_v, m_s; \
		unsigned long s = (i*256)/svw; \
		unsigned long v = (j*256)/svw; \
		m_v.m128i_i32[0] = v; /* - v v v */ \
		m_v = _mm_shuffle_epi32(m_v, 0); \
		m_a = _mm_slli_epi32(m_v, 8); \
		m_s.m128i_i32[0] = s; /* - s s s */ \
		m_s = _mm_shuffle_epi32(m_s, 0); \
		m_s = _mm_mullo_epi16(m_s, m_f); \
		m_s = _mm_srli_epi32(m_s, 8); \
		m_s = _mm_mullo_epi16(m_v, m_s); \
		m_b = _mm_sub_epi16(m_a, m_s); \
		m_s = _mm_srli_epi32(m_b, 8); \
		unsigned long P = m_s.m128i_i32[1]; \
		unsigned long q = m_s.m128i_i32[2]; \
		unsigned long t = m_s.m128i_i32[3]; \
		sv_data[p] = RR; \
		sv_data[p + 1] = GG; \
		sv_data[p + 2] = BB; \
	}}

	switch (modh) {
		case 0: COLORWHEEL(v, t, P) break;
		case 1: COLORWHEEL(q, v, P) break;
		case 2: COLORWHEEL(P, v, t) break;
		case 3: COLORWHEEL(P, q, v) break;
		case 4: COLORWHEEL(t, P, v) break;
		case 5: COLORWHEEL(v, P, q) break;
	}

PMULLW(ワード単位の積)に相当するダブルワードの演算がSSE4.1にならないと使えない(多分)らしく、
E6300までしか持っていないので、計算を2回に分けてワード単位に納めました。

しばらくデバッグし、なんとかMMXでも前と同じものが描画できるようになりました。
喜び勇んでホイールをくるくるすると…

変わらない。
CPUが50% (2コア中1コアのmax)近くまで使われます。配列部分のストアが悪いのか、とおもって描画サイズを上げてみると、驚愕の事実が。
どれだけ大きくしてもCPU負荷変わらず。しかも前のコードでもほぼ同じくらい。SV空間描画部分は実はたいして重くなかったようです。

別の箇所を切り離して、カラーピッカーだけで動かすと、
H/SVカラーピッカー
このくらいのサイズでCPU20%以内くらいでした。

引き続きwxWidgets 2.8.8を弄っていますが、妙なマクロを多用したり、移植性を高めるためのトリックを駆使しているらしく、綺麗にカスタムドローできない。
まあいざとなればWindows専用(wxMSW~)に限定して、WindowsAPIを生で叩けばいいんですが。

そのwxWidgetsを使う大きな利点の一つ、ドック/分離自在なwxAUIが時々落ちます。分離は問題無いものの、ドック時がやや弱いらしい。

さて、前回色相環とSV平面によるカラーピッカーを作りましたが、今回はRGBスライダ、混色スライダ、前景背景色セレクタ等を追加し、それらを連動出来るようにしてみました。
カラーピッカー
非Observerのリスト破棄なんかは呼び出しが何度も往復して頭が痛くなりそうです。

今はテストということで、マルチレイヤのファイルとしてはwxImageとそのラッパーをwxFileStreamで繋げただけのフォーマットを自作していますが、最終的にはPSDを扱いたい。しかしどうもWebにサンプルコードや最新のPhotoshopの資料が少なく、やっと見つけたMyPSDを試してみたところ複数のレイヤを別々に読むのは無理そうです。

そろそろ実際に描画するところを真面目に書くべきなんでしょうが、Undoとかマウス/タブレットの併存、過去のサンプリング点を用いた手ブレ補正など、かなり設計を練り込まないとあとで酷いことになりそうです。

そんな訳で久しぶりにデザインパターンの本を復習したりしていました。いつまで経ってもFactoryとかTemplateとつくようなパタンの名前が覚えられない。
使えれば良いという気もしますが、名付けることで構造を発見しやすくもなるはずで、記憶力のなさに辟易します。

2008 07 25

WinTabの練習

タブレットのAPI, WinTabを使ってみました。

あまりまとまった資料がなく、

などを参考に、試行錯誤しました。

クワッドディスプレイの上、システムの座標原点が中央にあるという、まるでテスト用にわざわざ作ったかのような環境で開発しているので、初っぱなから座標の関係が分からなくて苦労しました。

一応、簡単なポーリングモードのラッパー(C++)を置いておきます。tablet_20080725.zip

Favoしか持っていないので、傾きやら方向やらは未テスト。

ついでに(というかこちらが主目的?)作っている、ペイントアプリの概観。筆圧が効いています。
C++ + wxWidget. wxAUIというのがDock周りを全てやってくれるので、こういうソフトを書くのに向いていそう。
WinTabの練習

このカラーピッカー作るのには骨が折れました。C++の範囲では高速化したものの、MMXとかSSEの拡張命令は使っていないので、ホイール回すとSV空間の描画に少しもたつきます。

次のページ »