忍者ブログ
ADMINNEW ENTRY
C A L E N D A R
02 2024/03 04
S M T W T F S
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31

HP→http://sora-blue.net/~kuar/index.html
1 2 3 4 5 6 >>>
2024-03-19(Tue)
[PR]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。



2011-08-07(Sun)
[C++]あたかもメンバの別名のように振る舞う
あまりこういうことには出会わないとは思うが稀にメンバの名前を変えたいとか振る舞い方を変えたいとかあったりする。
例えばWindowsでのRECT構造体。
struct RECT{
 int left,top,right,bottom;
};
こいつをちょっと不便だから
struct Rect{
 int x,y,width,height;
};
にしたいと考える。
このままRectを拡張してしまえばいいじゃないかと思う人は話はここまでである。

しかしまあ、このまま拡張しても不便だから。特にRECTの参照渡しをする機会が多いのでその都度
RECT rect = Rect.RECT();
hoge(&rect);
とするのも面倒なわけである。

そのままダイレクトに引数に入れるために継承を行うとする。
struct Rect:public RECT{

};

メンバをどうするかである。プロパティとしてはx,y,width,heightとして扱いたい。
xとyに関しては少々不細工であってもleftとrightのポインタを持っておけばなんとかなりそうでもない。
struct Rect:public RECT{
 Rect():x(&left),y(&top){}
 int* x,y;
};
widthやheightはそうはいかない。widthはright-left、heightはbottom-topという式になる。

struct Rect:public RECT{
 Rect():x(&left),y(&top){}
 int width()const{return right-left;}
 int width(int newWidth){return right = left + newWidth;}
 int height()const{return bottom-top;}
 int height(int newHeight){return bottom = top + newHeight;}
 int* x,y;
};

形はどうであれこれでひとまずは、できたかのように思える。
しかしもっと自然に振舞いたいと思うわけである。
*rect.x = 1;
rect.width(3);
これを
rect.x = 1;
rect.width = 3;
などど記述できればまさにメンバの名前を変えたように振る舞っているといえよう。
PR


2011-02-21(Mon)
NSTextViewのカーソル位置を取得する
なかなか見つからないカーソル位置の取得関数。こんな関数名で実装されてたらそらわからんわなー。
NSTextFieldのカーソル位置も同じメソッドで取得することができる。
このRectはカーソルのフレームであるため、原点が左下のCocoaでは、y軸をheightだけ引く処理が必要になるであろう。
NSRect rect = [textView firstRectForCharacterRange:[textView selectedRange]];


2011-01-04(Tue)
無題
Cocoaを参考に自作しているFrameWorkの描画についての高速化の覚え書き。
まず前提として、Windowに対してViewが1枚貼られている。規定となるViewである。
CocoaでいうNSWindowのcontentLayerに該当する。
ちなみにこのViewは常にWindowsサイズと同じサイズになるようにWindowのサイズ変更が行われると、このViewのサイズも変更される。

Viewについてはレイヤー構造となっており、Viewが配列を有しており、addSubViewするとpush_backされる。
ViewはsuperViewへのポインタを格納している。無論subViewsも。因みにcontentLayerのsuperViewはNULL。
上の階層へ行くには連結リストを利用し、下の階層へは配列でアクセスすることになる。

描画はViewがもつdrawRect(Rect rect)で行う。Viewが持つdrawRect関数に関してはsubViewに対してdrawRect関数を呼ぶだけなので画像やテキストを描画する際にはViewを継承したImageViewやTextViewを利用する。

引数のrectが描画する四角形である。再描画処理は重いので必要最低限の範囲で行う。
例えばWindowサイズが800*600に対して再描画するViewが100*50だったなら、Viewの100*50だけ再描画する。

半透明などが絡むと混色処理が必要になるので再描画はcontentLayerからdrawRect関数を呼び出す。
また、contentLayerを持っているのはWindowなので実際には再描画はWindowに対して再描画するように命令を出す。

実際に関数はこんな感じ

struct Rect{
 Rect()x(0),y(0),width(0),height(0){}
 Rect(int _x,int _y ,int _width,int _height)x(_x),y(_y),width(_width),height(_height){}
 int x,y,width,height;
};
class Window{
public:
 virtual void drawRect(Rect rect);
private:
 View* m_contentLayer;
};
class View{
public:
 View(Rect rect);
 virtual void drawRect(Rect rect);
 virtual void repaint();
private:
 Rect m_rect;
 std::vector m_subViews;
};

void Window::drawRect(Rect rect){
 contentLayer->drawRect(rect);
}



void View::drawRect(Rect rect){
 // ここに何かしら描画処理
 std::vector::iterator it = m_subViews.begin();
 std::vector::iterator end = m_subViews.end();
 for(; it != end; ++it){
   (*it)->drawRect(rect);
 }
}

再描画関数に関しては自身のx,yからwidth,heightだけ再描画すればいいので

void View::repaint(){
 m_window->drawRect(m_frame);
}

となる。因みにcontentLayerはWindowへのポインタを持っており、addSubViewを呼び出すことにより、子にもwindowへのポインタが渡されるため、addSubViewされている全てのviewはwindowにアクセスできる。
画像に関してはもしも再描画したいViewが半透明ではない場合、contentLayerから描画するのは無意味である。
なぜなら混色が行われない以上、そのViewから下の階層のレイヤーしか見えないのだから。

void Window::drawRect(Rect rect,View* view){
 if(view == NULL){
  contentLayer->drawRect(rect);
 }else{
  view->drawRect(rect);
 }
}

この実装により、200枚ImageViewを貼ったとしても見える部分しか描画しないので処理は画像を2〜3枚貼る程度に収めることができる。
ただ、if文或いは三項条件演算子を通るコストはかかるが……。
実際にはsuperViewが該当のviewの背面に写るであろうviewとは限らない。
というと構造はsuperViewのsubViewsの中にviewそのものが格納されている。
もしviewがsuperViewから見てsubViewsの[1]以上であれば、superViewとviewの間に他のviewが存在する。
しかし、そのviewは再描画するべきviewと完全に重なっている保証はないため、完全に重なっているsuperViewに対して処理を行なっている。
最も、superViewのsubViewにviewが100枚とかあるとsuperViewとviewの間のviewへ問い合わせる処理にしないと最適化にならないが、
ここでは少量のviewしか扱わないことを前提としてこの実装にしている。

void View::repaint(){
 m_window->repaint(m_frame,this);
}

因みに不可視 or alphaが0なら描画の必要はないため

void View::drawRect(Rect rect){
 if(m_alpha != 0){
  //ここに何かしら描画処理
  std::vector::iterator it = m_subViews.begin();
  std::vector::iterator end = m_subViews.end();

  for(; it != end; ++it){
    (*it)->drawRect(rect);
  }
 }
}

自身が見えないのであれば当然subViewも見えないわけである。
見えるようにしたければ子の描画処理をifの外へ出してやるといい。ただ、不便なのは間違いない。

では、半透明だった場合を考える。半透明であれば混色が必要となる。
しかし半透明だから諦めてcontentLayerから描画するのは重すぎる。
なので半透明か否かをsuperViewへ問い合せて不透明ならそこから描画、半透明ならさらにsuperViewへ問い合わせ…ということにする。
連結リストなのでアクセスは遅いが描画をするよりかはマシだろう。
View* view::getSuperOpaqueView(){
 return (m_superView->getAlpha == 255) ? m_superView : m_superView->getSuperOpaqueView();
}
void View::repaint(){
 if(m_alpha == 255){
   m_window->drawRect(m_rect,this);
 }else{
   m_window->drawRect(m_rect,getSuperOpaqueView());
 }
}

ここでは不透明の値が255と定義されているのもとする。モノによっては1.0とかだったりするので状況に合わせて値を変えてほしい。

因みにこれは人によりけりであるが、addSubViewやsetAlpha、setVisible、setFrameなどViewを弄るような関数を利用する場合
自動的にrepaintするか否かである。基本的には自動でrepaintしたほうが便利だがこれでは高速化は望めない。
速さを気にする人なら明示的にrepaintを行うようにするといい。どっちもという人はwindowにisAutorepaintという関数をひとつ作って、局所的にするといい。

void addSubView(View* view){
 m_subViews.push_back(view);
 if(m_window->isAutoRepaint()){
  repaint();
 }
}

実際に使うときは
m_window->setAutoRepaint(false);
for(int i=0;i<100;++i){
 addSubView(new View(Rect(0,0,100,100)));
}
m_window->setAutoRepaint(true);
repaint();

とするとわりと高速に動作するようになる。
複数のWindowを扱う時を想定して、Windowのメンバ関数にしてある。
全Windowおかまいなしにしたいなら広域変数でいいだろう。

ここでは特に何も書いていないがdrawRect関数には本来ならば他にも複数の引数が絡むことになり、Windows32APIであれば、HDCもしくはHWNDが、DirectXであればLPDIRECT3D9、LPDIRECT3DDEVICE9などが入ることになるであろう。故にdrawRect関数そのものは直接呼び出すことはできないであろう。最も実装によっては可能と言えば可能だが難しいと私は思う。


2010-12-23(Thu)
無題
HPを更新しました。ツイッターの登場でますますブログとHPの影が薄いです。
mixiも殆どサンシャイン牧場やって他人の日記見るしかやってない!
ツイッターに殆ど書きたいことを書いてしまうのでこのブログもあまり意味をなしていない状況。
とりあえずはプログラムで詰まったところを書いとくみたいな感じではあるがそれもツイッターに取られてしまっている。

とりあえず今回はCALayerのcontentsについての記述。
CALayerのcontentsはどうもすぐに内容をreleaseしてしまうのかあとからaddSublayerするとBusErrorに。

layer.contents = (id)[image CGImage];

じゃあretainすりゃいいじゃんって思って
[[image CGImage] retain]
こいつでもダメ。

CGImageRefをretainする関数があったんだけどそれでもダメ。

どうしたもんかなー。


2010-04-11(Sun)
[VC++6]スクリーンセーバーのデバッグ設定
プロジェクト設定
・デバッグ
  実行ファイル:○○.exe->○○.scr
  プログラムの引数:/s

・リンク
  出力ファイル名:○○.exe->○○.scr

これでF5でスクリーンセーバーのデバッグができる。
ただし結局はOutputDebugStringやprintfが使えないので難儀である。
MessageBoxは使えるが使い物にはならないし必要ならファイル出力でもするか?


1 2 3 4 5 6 >>>

Powered by NinjaBlog. Template by うさねこ.
忍者ブログ[PR]