Mercurial > otakunoraifu
diff window/picture.cc @ 0:223b71206888
Initial import
author | thib |
---|---|
date | Fri, 01 Aug 2008 16:32:45 +0000 |
parents | |
children | 97b752b43502 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/window/picture.cc @@ -0,0 +1,1060 @@ +/* + * Copyright (c) 2004-2006 Kazunori "jagarl" Ueno + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include<stdio.h> +#include<vector> +#include<list> +#include<algorithm> + +#include"rect.h" +#include"event.h" +#include"font/font.h" +#include"font/text.h" +#include"SDL_rotozoom.h" +#include"system/file.h" + +#include"picture.h" + +using namespace std; + +int print_blit=0; +inline void dprintf(const char* fmt, ...) {} +/* render.cc */ +void DSurfaceBlitAlpha(Surface* src_o, const Rect& srcrect_o, Surface* dst_o, const Rect& dstrect_o, const unsigned char* alpha, const Rect& alpharect); +void DSurfaceBlitSaturate(Surface* src_o, const Rect& srcrect, Surface* dst_o, const Rect& dstrect, unsigned char alpha); +void DSurfaceBlitMultiply(Surface* src_o, const Rect& srcrect, Surface* dst_o, const Rect& dstrect); +void DSurfaceFill(Surface* src, const Rect& rect, int r, int g, int b, int a=0xff); // クリア +#if 0 /* DEBUG */ +#include<sys/types.h> +#include<sys/time.h> +#define dprintf printf +static struct timeval tv; +void gettm(void) { + gettimeofday(&tv, 0); +} +int calctm(void) { + struct timeval tv2; + gettimeofday(&tv2, 0); + int n = (tv2.tv_sec-tv.tv_sec)*100000 + (tv2.tv_usec-tv.tv_usec)/10; + return n; +} +#endif + +/****************************************** +** PicBase +*/ +PicBase::PicBase(const Rect& _rel_pos, PicContainer* _parent, int _attr) : + parent(_parent), rel_pos(_rel_pos), rel_solid_area(0,0,0,0), clip_area(0,0,0,0), + is_hidden(true), is_hidden_now(true), is_cached(false), attribute(_attr), surface_alpha_rect(0,0) { + + if (parent) root = parent->root; + else root = 0; + surface_back = 0; + surface_own = 0; + surface_alpha = 0; + surface_x = 0; surface_y = 0; + surface_w = -1; surface_h = -1; + widget = 0; + attribute |= NO_PICTURE; + if ( (attribute & CACHE_BACK) && root) { + surface_back = root->NewSurface(rel_pos.width(), rel_pos.height(), NO_MASK); + } + + if (parent) { + parent->children.push_back(this); + z_pos = parent->children.end(); z_pos--; + distance_root = parent->DistanceRoot() + 1; + } else { + distance_root = 1; + } +} +PicBase::~PicBase() { + ClearAnm(); + if (widget) { + fprintf(stderr,"Warning: PicBase::~PicBase: surface is disallocated but widget is still alive.\n"); + widget->deactivate(); + } + if (surface_back) root->DeleteSurface(surface_back); + if (surface_own && (attribute & SURFACE_FREE)) root->DeleteSurface(surface_own); + if (surface_alpha && (attribute & ALPHA_FREE)) delete surface_alpha; + iterator it; + if (parent) { // 自分を親から削除 + parent->children.remove(this); + // root の update 情報から自分を削除 + parent->Root().DeleteUpdatePic(this); + // 自分の領域を書き直す + Rect old_ppos = rel_pos; + parent->QueryAbsPos(old_ppos); + parent->ReBlit(old_ppos); + } +} +void PicBase::Blit(const Rect& rpos_orig) { + // 実際に描画する領域を得る + Rect rpos = rpos_orig; + // 親widget上に設定されたclip area 内に描画を限定する + if (clip_area.width() != 0) { + Rect clip = clip_area; + clip = child_pos(clip, this); + rpos.intersect(clip); + } + Rect apos = QueryAbsPos(rpos); + if (rpos.empty()) return; + // 必要に応じて保存、描画 + if (attribute & CACHE_BACK) root->BlitSurface(root->surface, apos, surface_back, rpos); + if (! (attribute & NO_PICTURE)) { + rpos.rmove(surface_x, surface_y); + if (surface_w >= 0 && surface_h >= 0) { + Rect clip(surface_x, surface_y, surface_x+surface_w, surface_y+surface_h); + rpos.intersect(clip); + } +//if (apos.ty < 200) fprintf(stderr,"Blit: %08x : (%d,%d,%d,%d) -> (%d,%d,%d,%d)\n",surface_own,rpos_orig.lx,rpos_orig.ty,rpos_orig.rx,rpos_orig.by,apos.lx,apos.ty,apos.rx,apos.by); + root->BlitSurface(surface_own, rpos, surface_alpha, surface_alpha_rect, root->surface, apos, attribute); + rpos.rmove(-surface_x, -surface_y); + } else if (parent == 0) { // 親がいないなら背景消去の責任をもつ + DSurfaceFill(root->surface, apos, 0, 0, 0); + } + PicContainer* cur = dynamic_cast<PicContainer*>(this); + if (cur && (!cur->children.empty())) { + cur->BlitChildren(rpos); + } +} +void PicBase::SimpleBlit(Surface* screen) { + // 実際に描画する領域を得る + Rect rpos(0, 0, rel_pos.width(), rel_pos.height()); + Rect apos = QueryAbsPos(rpos); + if (rpos.empty()) return; + rpos.rmove(surface_x, surface_y); + if (surface_w >= 0 && surface_h >= 0) { + Rect clip(surface_x, surface_y, surface_x+surface_w, surface_y+surface_h); + rpos.intersect(clip); + } +//if (apos.ty < 200) fprintf(stderr,"S-Blit: %08x : (%d,%d,%d,%d) -> (%d,%d,%d,%d)\n",surface_own,rel_pos.lx,rel_pos.ty,rel_pos.rx,rel_pos.by,apos.lx,apos.ty,apos.rx,apos.by); + root->BlitSurface(surface_own, rpos, surface_alpha, surface_alpha_rect, screen, apos, attribute); +} + +Rect PicBase::QueryAbsPos(Rect& rpos) { + rpos.intersect(Rect(0, 0, rel_pos.width(), rel_pos.height())); + if (parent == 0) { // root container + return rpos; + } + // 親の座標に変換後、Query する + Rect ppos = parent_pos(rpos); + Rect apos = parent->QueryAbsPos(ppos); + rpos = child_pos(ppos, this); + return apos; +}; +void PicBase::ReBlit(const Rect& rpos_c) { + Rect rpos = rpos_c; + Rect apos = QueryAbsPos(rpos); + + root->Update(this, rpos, apos); +} +void PicBase::ExecReBlit(const Rect& rpos_c) { + Rect rpos = rpos_c; + Rect abs_r = QueryAbsPos(rpos); + Rect ppos = parent_pos(rpos); +if(print_blit) fprintf(stderr,"back."); + if (parent) parent->BlitBack(z_pos, ppos); +if(print_blit) fprintf(stderr,"self."); + if (!is_hidden_now) Blit(rpos); +if(print_blit) fprintf(stderr,"front."); + if (parent) parent->BlitFront(z_pos, ppos); +if(print_blit) fprintf(stderr,"end."); +} + +void PicBase::ZMove(PicBase* move_to) { + if (parent == 0) { + fprintf(stderr,"Warning: PicBase::ZMove is called by root.\n"); + return; + } + if (move_to == ZMOVE_TOP) { + if (this == parent->children.back()) return; + } else if (move_to == ZMOVE_BOTTOM) { + if (this == parent->children.front()) return; + } else if (move_to == this) { + fprintf(stderr,"Error: PicBase::ZMove : move in front of itself!\n"); + return; + } else if (move_to && move_to->parent != parent) { + fprintf(stderr,"Error: PicBase::ZMove was called with a no-brother picture\n"); + return; + } + // move_to と zpos のうち、後ろの方の picture から書きなおす必要がある + iterator redraw_zpos = z_pos; redraw_zpos++; + if (move_to == ZMOVE_BOTTOM) { // 最背面へ + parent->children.erase(z_pos); + parent->children.push_front(this); + z_pos = parent->children.begin(); + redraw_zpos = parent->children.begin(); + } else if (move_to == ZMOVE_TOP) { // 最前面へ + redraw_zpos = z_pos; redraw_zpos++; + parent->children.erase(z_pos); + parent->children.push_back(this); + z_pos = parent->children.end(); z_pos--; + } else { + int dis_to = distance(move_to->parent->children.begin(), move_to->z_pos); + int dis_cur = distance(parent->children.begin(), z_pos); + if (dis_cur < dis_to) redraw_zpos = move_to->z_pos; + parent->children.erase(z_pos); + iterator insert_pos = move_to->z_pos; insert_pos++; + parent->children.insert(insert_pos, this); + z_pos = move_to->z_pos; z_pos++; + } + if (! is_hidden_now) { + is_cached = false; + ReBlit(); + /* @@@ parent->Blit() と Blit() の違いが分からないので修正 06/12/02 + Rect ppos = rel_pos; + parent->QueryAbsPos(ppos); + parent->ReBlit(ppos); + */ + } +}; +void PicBase::RMove(int add_x, int add_y) { + Rect old_ppos = rel_pos; + rel_pos.rmove(add_x, add_y); + parent->QueryAbsPos(old_ppos); + parent->ReBlit(old_ppos); + ReBlit(); + + if (widget) { + Rect new_ppos = rel_pos; + Rect new_apos = parent->QueryAbsPos(new_ppos); + widget->SetRegion(new_apos); + } +} +void PicBase::Move(int new_rx, int new_ry) { + RMove(new_rx-rel_pos.lx, new_ry-rel_pos.ty); +} +void PicBase::SetEventWidget(PicWidget* new_widget) { + widget = new_widget; + if (widget) { + Rect new_ppos = rel_pos; + Rect apos = parent->QueryAbsPos(new_ppos); + widget->SetRegion(apos); + } +} +void PicBase::show_all(void) { + PicContainer* cont = dynamic_cast<PicContainer*>(this); + if (cont && (!cont->children.empty())) cont->set_showflag(); + show(); +} +bool PicBase::IsParent(PicBase* to) { + if (parent == 0) return false; + if (parent == to) return true; + return parent->IsParent(to); +} +void PicBase::show(void) { + /* 自分の親がすべて shown か? */ + PicContainer* cur; + for (cur = parent; cur != 0; cur = cur->parent) + if (cur->is_hidden) break; + if (cur) { // 親が隠れているので表示はしない + is_hidden = false; + is_hidden_now = true; + return; + } + if (is_hidden == false) return; // すでに表示されているのでなにもしない + if (widget) { + widget->activate(); + } + is_hidden = false; + is_hidden_now = false; + is_cached = false; + cur = dynamic_cast<PicContainer*>(this); + if (cur && (!cur->children.empty())) cur->set_nowhiddenflag(false); + ReBlit(); +} +void PicBase::hide(void) { + if (is_hidden) return; + if (widget) { + widget->deactivate(); + } + is_hidden = true; + is_hidden_now = true; + is_cached = false; + PicContainer* cur = dynamic_cast<PicContainer*>(this); + if (cur && (!cur->children.empty())) cur->set_nowhiddenflag(true); + ReBlit(); +} +void PicBase::SetSurfaceAlpha(const unsigned char* alpha, const Rect& alpha_r) { + if (attribute & ALPHA_FREE) { + if (surface_alpha) delete[] surface_alpha; + surface_alpha = 0; + } + surface_alpha = alpha; + surface_alpha_rect = alpha_r; + if (!is_hidden) ReBlit(); +} +void PicBase::SetSurfaceColorKey(int r, int g, int b) { + surface_alpha = 0; + surface_alpha_rect = Rect(0,0); + attribute &= ~(BLIT_SATURATE | BLIT_MULTIPLY); + if (surface_own) { + int key = SDL_MapRGB( ((SDL_Surface*)surface_own)->format, r, g, b); + key |= 0xff000000; + SDL_SetColorKey( (SDL_Surface*)surface_own, SDL_SRCCOLORKEY, key); + } + if (!is_hidden) ReBlit(); +} +void PicBase::SetSurfaceAlphaFile(const char* file) { + + /* ファイルを元に alpha 画像を作成する */ + /* ファイル: パルフェの 'fil' ファイル */ + ARCINFO* info = file_searcher.Find(FILESEARCH::PDT, file,"fil"); + if (info == 0) return; + char* new_alpha = info->CopyRead(); + int alpha_size = info->Size(); + delete info; + Rect sr(0,0); int w,h; + if (surface_own == 0 || new_alpha == 0) { +err_ret: + if (new_alpha) delete[] new_alpha; + SetSurfaceAlpha(0,Rect(0,0)); + return; + } + sr = Rect(*surface_own); + w = sr.width(); + h = sr.height(); + if (alpha_size < w*h) goto err_ret; + int i,j; + if ( ((SDL_Surface*)surface_own)->format->Amask == 0) { // mask を surface に繰り込む + Surface* dest = root->NewSurface(w,h, ALPHA_MASK); + for (i=0; i<h; i++) { + char* a = new_alpha + w*i; + char* s = (char*)((SDL_Surface*)surface_own)->pixels + ((SDL_Surface*)surface_own)->pitch*i; + char* d = (char*)((SDL_Surface*)dest)->pixels + ((SDL_Surface*)dest)->pitch*i; + int sbpp = ((SDL_Surface*)surface_own)->format->BytesPerPixel; + int dbpp = ((SDL_Surface*)dest)->format->BytesPerPixel; + + for (j=0; j<w; j++) { + int d = read_little_endian_int(s); + d &= 0xffffff; + if (d == 0) ; + else if (*a == 0) d |= 0xff000000; + else d |= (int(*a) << 24); + s += sbpp; d += dbpp; a++; + } + } + delete new_alpha; + root->DeleteSurface(surface_own); + surface_own = dest; + SetSurfaceAlpha(0, Rect(0,0)); + } else { // 外部にマスク作成 + /* マスクのうち、0xff であるべき部分を画像から判別、変更する */ + for (i=0; i<h; i++) { + char* a = new_alpha + w*i; + char* s = (char*)((SDL_Surface*)surface_own)->pixels + ((SDL_Surface*)surface_own)->pitch*i; + int bpp = ((SDL_Surface*)surface_own)->format->BytesPerPixel; + for (j=0; j<w; j++) { + if ( ((*(int*)s) & 0xffffff) == 0) *a = 0; + else if (*a == 0) *a = 0xff; + s += bpp; a++; + } + } + SetSurfaceAlpha( (unsigned char*)new_alpha, Rect(0,0,w,h)); + attribute |= ALPHA_FREE; + } +} +void PicBase::SetSurface(const char* filename, int x, int y) { + Surface* s = root->NewSurface(filename); + SetSurface(s, x, y, SURFACE_FREE); +} +void PicBase::SetSurface(Surface* new_surface, int x, int y, int new_attr) { + if (surface_own && (attribute & SURFACE_FREE)) { + root->DeleteSurface(surface_own); + } + attribute &= ~(SURFACE_FREE | BLIT_SATURATE | BLIT_MULTIPLY | NO_PICTURE | SOLID); + attribute |= new_attr; + surface_own = new_surface; + surface_x = x; + surface_y = y; + surface_w = -1; + surface_h = -1; + + if (attribute & FIT_SURFACE) { + // surface の大きさに自分の大きさを変更 + parent->ReBlit(rel_pos); + if (surface_own == 0) { + rel_pos = Rect(rel_pos.lx, rel_pos.ty); + } else { + Rect r(*surface_own); + int w = r.width(), h = r.height(); + w -= x; h -= y; + rel_pos = Rect(rel_pos.lx, rel_pos.ty, rel_pos.lx+w, rel_pos.ty+h); + } + } + + rel_solid_area = Rect(0,0,0,0); + if (! surface_own) attribute |= NO_PICTURE; + else if (root->with_mask(surface_own) == 0) { + attribute |= SOLID; + rel_solid_area = rel_pos; + } + if (!is_hidden) ReBlit(); +} +void PicBase::SetSurfacePos(int x, int y) { + if (surface_x == x && surface_y == y && surface_w == -1 && surface_h == -1) return; + surface_x = x; surface_y = y; + surface_w = -1; surface_h = -1; + if (!is_hidden_now) ReBlit(); +} +int PicBase::SurfacePosX(void) { + return surface_x; +} +int PicBase::SurfacePosY(void) { + return surface_y; +} +void PicBase::SetSurfaceRect(const Rect& r) { + if (surface_x == r.lx && surface_y == r.ty && surface_w == r.width() && surface_h == r.height()) return; + surface_x = r.lx; surface_y = r.ty; + surface_w = r.width(); surface_h = r.height(); + parent->ReBlit(rel_pos); + rel_pos = Rect(rel_pos.lx, rel_pos.ty, rel_pos.lx+surface_w, rel_pos.ty+surface_h); + if (widget) { + Rect new_ppos = rel_pos; + Rect apos = parent->QueryAbsPos(new_ppos); + widget->SetRegion(apos); + } + if (!is_hidden_now) ReBlit(); +} +void PicBase::SetClipArea(const Rect& r) { + if (clip_area == r) return; + clip_area = r; + parent->ReBlit(rel_pos); +} + +void PicBase::SetSurfaceAttribute(int new_attribute) { + attribute &= ~(BLIT_SATURATE | BLIT_MULTIPLY); + attribute |= new_attribute & (BLIT_SATURATE | BLIT_MULTIPLY); + if (new_attribute & (BLIT_SATURATE | BLIT_MULTIPLY)) { + rel_solid_area = Rect(0,0); + } +} +void PicBase::SetSurfaceFreeFlag(bool flag) { + if (flag) attribute |= SURFACE_FREE; + else attribute &= ~SURFACE_FREE; + +} + +/****************************************** +** PicContainer +*/ +PicContainer::PicContainer(const Rect& rel_pos, PicContainer* parent, int attr) : + PicBase(rel_pos, parent, attr) { +} +PicContainer::~PicContainer() { + iterator end = children.end(); + for (iterator it = children.begin(); it != end; ) { + iterator it_next = it; it_next++; + if ((*it)->widget) delete (*it)->widget; // picture にwidget が付属しているなら、そちらをdelete + else delete (*it); + it = it_next; + } +} +void PicContainer::BlitBack(iterator z, Rect rpos) { + rpos.intersect(Rect(0, 0, rel_pos.width(), rel_pos.height())); + if (rpos.empty()) return; + iterator end = children.end(), begin = children.begin(); iterator it = begin; + + Rect ppos = parent_pos(rpos); + if (is_hidden_now) goto parent_redraw; + // cache されている領域を探す + // z自身がキャッシュしていれば、ここで終了 + if ( ((*z)->attribute & CACHE_BACK) && ( (*z)->is_cached) && (*z)->rel_pos.is_inner(rpos)) { + Rect cpos = child_pos(rpos, *z); + Rect apos = (*z)->QueryAbsPos(cpos); + root->BlitSurface( (*z)->surface_back, cpos, root->surface, apos); + return; + } + // z より下の子がキャッシュ、あるいは SOLID 描画できないか? + for (it = z; it != begin;) { // 子がcontainerの場合のチェックは省略 + it--; + if ( (*it)->is_hidden_now) continue; + if ( (*it)->rel_pos.is_crossed(rpos)) { + if ( ((*it)->attribute & CACHE_BACK) && ((*it)->is_cached) && (*it)->rel_pos.is_inner(rpos)) { + Rect cpos = child_pos(rpos, *it); + Rect apos = (*it)->QueryAbsPos(cpos); + root->BlitSurface( (*it)->surface_back, cpos, root->surface, apos); + goto children_redraw; + } + if ( (*it)->rel_solid_area.is_inner(rpos)) { + goto children_redraw; + } + } + } + // 自分自身がキャッシュ、あるいは SOLID 描画できないか? + if (rel_solid_area.is_inner(ppos)) { + goto self_redraw; + } + if ( (attribute & CACHE_BACK) && is_cached) { + Rect cpos = child_pos(rpos, *z); + Rect apos = (*z)->QueryAbsPos(cpos); + Rect draw_rpos = (*z)->parent_pos(cpos); +if(print_blit) fprintf(stderr,"cahce."); + root->BlitSurface(surface_back, draw_rpos, root->surface, apos); + goto self_redraw; + } +parent_redraw: + if (parent) { + Rect ppos = parent_pos(rpos); +if(print_blit) fprintf(stderr,"parent-back."); + parent->BlitBack(z_pos, ppos); + } + if (is_hidden_now) return; +self_redraw: +if(print_blit) fprintf(stderr,"back-self."); + BlitSelf(rpos); // 子は描画せず、自分だけ描画 +children_redraw: + for (; it != z; it++) { + if ( (*it)->is_hidden_now) continue; + if ( (*it)->rel_pos.is_crossed(rpos)) { + Rect cpos = child_pos(rpos, *it); + (*it)->Blit(cpos); + } + } +} +void PicContainer::BlitChildren(Rect rpos) { +if (print_blit) fprintf(stderr,"bc."); + iterator end = children.end(); + for (iterator it = children.begin(); it != end; it++) { +if ( (*it)->is_hidden_now) if(print_blit) fprintf(stderr,"bch %08x;",*it); + if ( (*it)->is_hidden_now) continue; + if ( (*it)->rel_pos.is_crossed(rpos)) { + Rect cpos = child_pos(rpos, *it); + (*it)->Blit(cpos); + } + } +} +void PicContainer::BlitFront(iterator z, Rect rpos) { + rpos.intersect(Rect(0, 0, rel_pos.width(), rel_pos.height())); + if (rpos.empty()) return; + iterator end = children.end(); iterator it; + z++; + for (it = z; it != end; it++) { + if ( (*it)->is_hidden_now) continue; + if ( (*it)->rel_pos.is_crossed(rpos)) { + Rect cpos = child_pos(rpos, *it); + (*it)->Blit(cpos); + } + } + if (parent) { + Rect ppos = parent_pos(rpos); + parent->BlitFront(z_pos, ppos); + } +}; +void PicContainer::BlitSelf(Rect rpos) { + // 実際に描画する領域を得る + rpos.intersect(Rect(0, 0, rel_pos.width(), rel_pos.height())); + if (rpos.empty()) return; + Rect apos = QueryAbsPos(rpos); + // 必要に応じて保存、描画 +if(print_blit) fprintf(stderr,"self-back."); + if (attribute & CACHE_BACK) root->BlitSurface(root->surface, apos, surface_back, rpos); + if (! (attribute & NO_PICTURE)) { + rpos.rmove(surface_x, surface_y); + if (surface_w >= 0 && surface_h >= 0) { + Rect clip(0, 0, surface_w, surface_h); + clip.rmove(rpos.lx, rpos.ty); + rpos.intersect(clip); + } +if(print_blit) fprintf(stderr,"self-blit."); + root->BlitSurface(surface_own, rpos, surface_alpha, surface_alpha_rect, root->surface, apos, attribute); + } else if (parent == 0) { // 親がいないなら背景消去の責任をもつ + DSurfaceFill(root->surface, apos, 0, 0, 0); + } +} + +void PicContainer::set_showflag(void) { + iterator end = children.end(); + for (iterator it = children.begin(); it != end; it++) { + (*it)->is_hidden = false; + PicContainer* next = dynamic_cast<PicContainer*>(*it); + if (next && (!next->children.empty())) next->set_showflag(); + } +} +void PicContainer::set_nowhiddenflag(bool is_hide) { + iterator end = children.end(); + for (iterator it = children.begin(); it != end; it++) { + if (is_hide) (*it)->is_hidden_now = true; + else (*it)->is_hidden_now = (*it)->is_hidden; + if ( (*it)->widget) { + if ((*it)->is_hidden_now) (*it)->widget->deactivate(); + else (*it)->widget->activate(); + } + PicContainer* next = dynamic_cast<PicContainer*>(*it); + if (next && (!next->children.empty())) next->set_nowhiddenflag(is_hide); + } +} +void PicContainer::RMove(int add_x, int add_y) { // event widget の移動があり得るので子についてもRMoveを呼び出す + PicBase::RMove(add_x, add_y); + iterator end = children.end(); + for (iterator it = children.begin(); it != end; it++) { + (*it)->RMove(0,0); + } +} + +PicBase* PicContainer::create_leaf(const Rect& rel_pos, int attr) { + return new PicBase(rel_pos, this, attr); +} +PicContainer* PicContainer::create_node(const Rect& rel_pos, int attr) { + return new PicContainer(rel_pos, this, attr); +} + +/*************************************************************** +** +** PicWidget +*/ + +PicWidget::PicWidget(void) { + pic = 0; +} +PicWidget::~PicWidget() { + if (pic) { + pic->SetEventWidget(0); + delete pic; + } + pic = 0; +} +void PicWidget::SetPic(PicBase* new_pic) { + if (pic) { + pic->SetEventWidget(0); + delete pic; + } + pic = new_pic; + if (pic) pic->SetEventWidget(this); +} +PicBase* PicWidget::Pic(void) { + if (pic == 0) { + fprintf(stderr,"Error: PicWidget::Pic returns zero.\n"); + } + return pic; +} +PicContainer* PicWidget::PicNode(void) { + PicContainer* node = dynamic_cast<PicContainer*>(pic); + if (node == 0) { + fprintf(stderr,"Error: PicWidget::PicNode returns zero.\n"); + } + return node; +} + +/****************************************** +** FileToSurface +*/ + +#include<list> +#include<map> +#include<string> +using namespace std; +struct SurfaceIndex { + typedef list<SurfaceIndex*>::iterator qiterator; + string filename; + Surface* surface; + qiterator qpos; + int ref_count; +}; + +class FileToSurface { + typedef list<SurfaceIndex*>::iterator qiterator; + + list<SurfaceIndex*> queue; + map<string, SurfaceIndex*> findex; + map<Surface*, SurfaceIndex*> mindex; + int count; + int count_max; + const PicRoot& root; + bool DeleteData(SurfaceIndex* data); + Surface* LoadSurface(string name, char*& mem); +public: + FileToSurface(const PicRoot& root); + ~FileToSurface(void); + Surface* Load(string name); + bool Free(Surface* s); +}; + +FileToSurface::FileToSurface(const PicRoot& _root) : root(_root) { + count = 0; + count_max = 32; // キャッシュ量(決め打ち) +}; +FileToSurface::~FileToSurface() { + qiterator it; + for (it=queue.begin(); it != queue.end(); it++) { + if ( (*it)->ref_count) { + fprintf(stderr, "Warning: FileToSurface: delete referenced surface named '%s'\n",(*it)->filename.c_str()); + } + root.DeleteSurfaceImpl( (*it)->surface); + delete *it; + } +} +inline bool FileToSurface::DeleteData(SurfaceIndex* data) { + if ( data->ref_count) return false; + findex.erase(data->filename); + mindex.erase(data->surface); + queue.erase(data->qpos); + root.DeleteSurfaceImpl(data->surface); + delete data; + count--; + return true; +} +inline Surface* FileToSurface::LoadSurface(string name, char*& mem) { + ARCINFO* info = file_searcher.Find(FILESEARCH::PDT, name.c_str(),"pdt"); + if (info == 0) return 0; + GRPCONV* conv = GRPCONV::AssignConverter(info); + if (conv == 0) { delete info;return 0;} + mem = (char*)malloc(conv->Width() * conv->Height() * 4 + 1024); + Surface* s = 0; + if (conv->Read(mem)) { + MaskType is_mask = conv->IsMask() ? ALPHA_MASK : NO_MASK; + if (is_mask == ALPHA_MASK) { // alpha がすべて 0xff ならマスク無しとする + int len = conv->Width()*conv->Height(); + unsigned int* d = (unsigned int*)mem; + int i; for (i=0; i<len; i++) { + if ( (*d&0xff000000) != 0xff000000) break; + d++; + } + if (i == len) { + is_mask = NO_MASK; + } + } + s = root.NewSurfaceFromRGBAData(conv->Width(), conv->Height(), mem, is_mask); + } + delete conv; delete info; // delete data; + return s; +} +Surface* FileToSurface::Load(string name) { + if (findex.find(name) != findex.end()) { + findex[name]->ref_count++; + return findex[name]->surface; + } + char* mem; + Surface* surface = LoadSurface(name, mem); + if (surface == 0) return 0; + + while (count >= count_max) { // count_max 以上のデータを可能なら削除する + qiterator it; + for (it=queue.begin(); it != queue.end(); it++) { + if (DeleteData(*it)) break; + } + if (it == queue.end()) break; // 全データが使用中なら終了 + } + SurfaceIndex* new_index = new SurfaceIndex; + new_index->filename = name; + new_index->surface = surface; + findex[name] = new_index; + mindex[surface] = new_index; + queue.push_back(new_index); + new_index->qpos = queue.end(); new_index->qpos--; + new_index->ref_count = 1; + count++; + return surface; +} +bool FileToSurface::Free(Surface* s) { + if (mindex.find(s) == mindex.end()) { + return false; + } + SurfaceIndex* index = mindex[s]; + if (index->ref_count == 0) DeleteData(index); + else index->ref_count--; + return true; +} + +/****************************************** +** PicRoot +*/ +#include<SDL.h> + +#include"surface.h" + +#define DefaultRmask 0xff0000 +#define DefaultGmask 0xff00 +#define DefaultBmask 0xff +#define DefaultAmask 0xff000000 +#define DefaultBpp 32 + +PicRoot::PicRoot(void) { + hw_surface = (Surface*)SDL_GetVideoSurface(); + SDL_PixelFormat* fmt_SDL = hw_surface->format; + if (fmt_SDL->BitsPerPixel == DefaultBpp && fmt_SDL->Rmask == DefaultRmask && fmt_SDL->Gmask == DefaultGmask && fmt_SDL->Bmask == DefaultBmask) { + surface = hw_surface; + } else { + surface = (Surface*)SDL_CreateRGBSurface(0, hw_surface->w, hw_surface->h, DefaultBpp, DefaultRmask, DefaultGmask, DefaultBmask, 0); + } + + Rect rpos(0, 0, surface->w, surface->h); + root = new PicContainer(rpos, 0, 0); + root->InitRoot(this); + root->show(); + ftosurface = new FileToSurface(*this); + width = surface->w; + height = surface->h; + return; +} +PicRoot::~PicRoot() { + // if (surface) DeleteSurfaceImpl(surface); // SDL_GetVideoSurface() した surface は開放の必要がないらしい + surface = 0; + delete root; + delete ftosurface; +} +void PicRoot::Update(PicBase* pic, const Rect& rpos, const Rect& apos) { + update_rects.push_back(UpdateItem(pic, rpos, apos)); +} +bool PicRoot::UpdateItem::less(const PicRoot::UpdateItem& a, const PicRoot::UpdateItem& b) { + return a.pic->DistanceRoot() < b.pic->DistanceRoot(); +} +void PicRoot::DeleteUpdatePic(PicBase* pic) { + vector<UpdateItem>::iterator it = update_rects.begin(); + while(it != update_rects.end()) { + if (it->pic == pic) { + update_rects.erase(it); + it = update_rects.begin(); + continue; + } + it++; + } + return; +} +void PicRoot::ExecUpdate(void) { + /* 共通する領域を消去する */ + sort(update_rects.begin(), update_rects.end(), UpdateItem::less); + vector<UpdateItem>::iterator it; + vector<UpdateItem>::iterator end = update_rects.end(); +if(print_blit){ + fprintf(stderr,"ExecUpdate Start: \n\t"); + for (it=update_rects.begin(); it != end; it++) { + fprintf(stderr,"(%d,%d,%d,%d), ",it->apos.lx,it->apos.ty,it->apos.rx,it->apos.by); + } + fprintf(stderr,"\n"); +} + for (it=update_rects.begin(); it != end; it++) { + if (it->rpos.width() == 0) continue; + + Rect apos = it->apos; + PicBase* pic = it->pic; + + vector<UpdateItem>::iterator jt = it; jt++; + for (; jt != end; jt++) { + if (apos.is_inner(jt->apos)) { + if (jt->pic == pic || jt->pic->IsParent(pic)) { // 親が共通、かつ領域も共通 + jt->rpos = Rect(0,0); // empty rect をセット + jt->apos = Rect(0,0); + } + } else if (jt->apos.is_inner(apos)) { // 相手に自分が包含される + if (jt->pic == pic || jt->pic->IsParent(pic)) { // 親が共通、かつ領域も共通 + it->rpos = Rect(0,0); + it->apos = Rect(0,0); + break; + } + } + } + } +if(print_blit){ + fprintf(stderr,"->\t"); + for (it=update_rects.begin(); it != end; it++) { + fprintf(stderr,"(%d,%d,%d,%d), ",it->apos.lx,it->apos.ty,it->apos.rx,it->apos.by); + } + fprintf(stderr,"\n"); +} + + int num = update_rects.size(); + SDL_Rect* r = new SDL_Rect[num]; + Rect confine = Rect(0, 0, surface->w, surface->h); + int n = 0; + int i; + for (i=0; i<num; i++) { + UpdateItem& item = update_rects[i]; + Rect& ur = item.apos; + if (ur.width() == 0) continue; +if(print_blit)fprintf(stderr,"%08x: %d,%d,%d,%d",item.pic, item.apos.lx, item.apos.ty, item.apos.rx, item.apos.by); + + item.pic->ExecReBlit(item.rpos); +if(print_blit)fprintf(stderr,"\n"); + ur.intersect(confine); + r[n].x = ur.lx; + r[n].y = ur.ty; + r[n].w = ur.rx - ur.lx; + r[n].h = ur.by - ur.ty; + if (surface != hw_surface) SDL_BlitSurface(surface, &r[n], hw_surface, &r[n]); + n++; + } +if(print_blit)fprintf(stderr,"\n"); + SDL_UpdateRects(hw_surface, n, r); + delete[] r; update_rects.clear(); +} + +Surface* PicRoot::NewSurface(int w, int h, MaskType with_mask) const { + Surface* s; + if (with_mask == ALPHA_MASK) { + s = (Surface*)SDL_CreateRGBSurface(SDL_SRCALPHA, w, h, DefaultBpp, DefaultRmask, DefaultGmask, DefaultBmask, DefaultAmask); + } else { + s = (Surface*)SDL_CreateRGBSurface(0, w, h, DefaultBpp, DefaultRmask, DefaultGmask, DefaultBmask, 0); + } + return s; +} + +Surface* PicRoot::NewSurfaceFromRGBAData(int w, int h, char* data, MaskType with_mask) const { + int amask = (with_mask == ALPHA_MASK) ? DefaultAmask : 0; + Surface* s = (Surface*)SDL_CreateRGBSurfaceFrom(data, w, h, DefaultBpp, w*4, DefaultRmask, DefaultGmask, DefaultBmask, amask); + s->flags &= ~SDL_PREALLOC; + return s; +}; +Surface* PicRoot::NewSurface(const char* f, MaskType with_mask) { + if (f == 0) return 0; + Surface* s = ftosurface->Load(f); + if (s == 0) return 0; + if (with_mask == COLOR_MASK) { + SDL_SetColorKey( (SDL_Surface*)s, SDL_SRCCOLORKEY, *(Uint32*)s->pixels); + } + /* xkanon の残骸 */ + if (strcmp(f, "grdat") == 0) + SDL_SetColorKey(s, SDL_SRCCOLORKEY, 0x55aa66); + return s; +} +Surface* PicRoot::RotZoomSurface(Surface* from, double zoom, double rotate) { + Surface* ret = (Surface*)rotozoomSurface( (SDL_Surface*)from, rotate, zoom, SMOOTHING_OFF); + return ret; +} +void PicRoot::DeleteSurfaceImpl(Surface* s) const { + SDL_FreeSurface(s); +} +void PicRoot::DeleteSurface(Surface* s) { + if (!ftosurface->Free(s)) + DeleteSurfaceImpl(s); +} +inline SDL_Rect SDLed(const Rect& rect) { + SDL_Rect r; + r.x = rect.lx; + r.y = rect.ty; + r.w = rect.rx-rect.lx; + r.h = rect.by-rect.ty; + return r; +} + +#ifndef ALPHA_MAX +#define ALPHA_MAX 255 +#endif +void PicRoot::BlitSurface(Surface* src, const Rect& src_r, const unsigned char* alpha, const Rect& alpha_r, Surface* dest, const Rect& dest_r, int attribute) const { +if (print_blit) fprintf(stderr," s %08x %d:%d:%d:%d;",src, dest_r.lx, dest_r.ty, dest_r.rx, dest_r.by); + SDL_Rect sr = SDLed(src_r); SDL_Rect dr = SDLed(dest_r); +special_blit: + if (attribute & PicBase::BLIT_MULTIPLY) { +if (print_blit) fprintf(stderr,"M"); + DSurfaceBlitMultiply(src, src_r, dest, dest_r); + return; + } else if (attribute & PicBase::BLIT_SATURATE) { + if (src->format->Amask != 0) goto normal_blit; +if (print_blit) fprintf(stderr,"S"); + unsigned char a = 255; + if (alpha && alpha_r.width() >= 1 && alpha_r.height() >= 1) a = *alpha; + DSurfaceBlitSaturate(src, src_r, dest, dest_r, a); + return; + } +normal_blit: +if (print_blit) fprintf(stderr,"N"); + if (alpha == 0 || alpha_r.width() == 0) { // simple blit +if (print_blit) fprintf(stderr,"X"); + SDL_BlitSurface(src, &sr, dest, &dr); + return; + } + if (alpha_r.width() == 1 && alpha_r.height() == 1) { + if (*alpha == 255) { +if (print_blit) fprintf(stderr,"Y"); + SDL_BlitSurface(src, &sr, dest, &dr); + return; + } + if (src->format->Amask == 0) { // use per-surface alpha +if (print_blit) fprintf(stderr,"Z"); + SDL_SetAlpha(src, SDL_SRCALPHA, *alpha); + SDL_BlitSurface(src, &sr, dest, &dr); + SDL_SetAlpha(src, 0, 0); + return; + } + } + // generic alpha blit +if (print_blit) fprintf(stderr,"W"); + DSurfaceBlitAlpha(src, src_r, dest, dest_r, alpha, alpha_r); + return; +} + +bool PicRoot::with_mask(Surface* s) { + return s->format->Amask != 0; +} + +#if USE_X11 +#include<SDL_syswm.h> +#include<X11/Xlib.h> +#include<X11/Xutil.h> +#endif /* USE_X11 */ +void PicRoot::SetWindowCaption(const char* caption) { +#if USE_X11 +// SDL_WM_SetCaption(caption, 0); + // SDLの関数では2バイト文字をサポートしてくれないので、同等の内容に修正 + SDL_SysWMinfo info; + memset(&info,0,sizeof(info)); + SDL_VERSION(&(info.version)); + if (SDL_GetWMInfo(&info) == 1) { + Display* display = info.info.x11.display; + Window wm = info.info.x11.wmwindow; + if (wm == 0) wm = info.info.x11.window; + if (wm != 0) { + XTextProperty titleprop; + XmbTextListToTextProperty(display, (char**)&caption, 1, XCompoundTextStyle, &titleprop); + XSetWMName(display, wm, &titleprop); + XSetWMIconName(display, wm, &titleprop); + XFree(titleprop.value); + } + XSync(display, False); + } +#endif /* USE_X11 */ +} + +/************************************************************ +** PicAnm +*/ + +void PicBase::ClearAnm(void) { + while(!anm.empty()) { + delete anm.back(); + } +} +PicAnm::PicAnm(PicBase* _pic) { + pic.push_back(_pic); + pic[0]->anm.push_back(this); + return; + +} +PicAnm::PicAnm(vector<PicBase*> _pic) : pic(_pic) { + if (pic.empty()) return; + pic[0]->anm.push_back(this); + return; +} +PicAnm::~PicAnm() { + vector<PicAnm*>::iterator it = find(pic[0]->anm.begin(), pic[0]->anm.end(), this); + if (it == pic[0]->anm.end()) { + fprintf(stderr,"Cannot found this in PicAnm::~PicAnm()"); + } else { + pic[0]->anm.erase(it); + } +}