Mercurial > otakunoraifu
view window/picture.cc @ 60:e16e13d8cd68
Replaced SATURATE -> ADD, implemented objComposite, corrected minor things
author | Thibaut GIRKA <thib@sitedethib.com> |
---|---|
date | Fri, 18 Dec 2009 20:41:38 +0100 |
parents | ddbcbd000206 |
children | 4416cfac86ae |
line wrap: on
line source
/* * 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; /* 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 DSurfaceBlitAdd(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 = NULL; surface_back = NULL; surface_own = NULL; surface_alpha = 0; surface_x = 0; surface_y = 0; surface_w = -1; surface_h = -1; widget = NULL; 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 != NULL) { fprintf(stderr,"Warning: PicBase::~PicBase: surface is disallocated but widget is still alive.\n"); widget->deactivate(); } if (surface_back != NULL) root->DeleteSurface(surface_back); if (surface_own != NULL && (attribute & SURFACE_FREE)) root->DeleteSurface(surface_own); if (surface_alpha != NULL && (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: %p : (%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: %p : (%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 == NULL) { // 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 (parent) parent->BlitBack(z_pos, ppos); if (!is_hidden_now) Blit(rpos); if (parent) parent->BlitFront(z_pos, ppos); } void PicBase::ZMove(PicBase* move_to) { if (parent == NULL) { 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 != NULL) { 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 != NULL) { 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 != NULL) { // 親が隠れているので表示はしない is_hidden = false; is_hidden_now = true; return; } if (is_hidden == false) return; // すでに表示されているのでなにもしない if (widget != NULL) { 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 != NULL) { 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_ADD | 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 = FileSearcher::GetInstance()->Find(FileSearcher::PDT, file,"fil"); if (info == NULL) return; char* new_alpha = info->CopyRead(); int alpha_size = info->Size(); delete info; Rect sr(0,0); int w,h; if (surface_own == NULL || new_alpha == NULL) { 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 != NULL && (attribute & SURFACE_FREE)) { root->DeleteSurface(surface_own); } attribute &= ~(SURFACE_FREE | BLIT_ADD | 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 == NULL) 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 != NULL) { 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_ADD | BLIT_MULTIPLY); attribute |= new_attribute & (BLIT_ADD | BLIT_MULTIPLY); if (new_attribute & (BLIT_ADD | 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 != NULL) 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); root->BlitSurface(surface_back, draw_rpos, root->surface, apos); goto self_redraw; } parent_redraw: if (parent) { Rect ppos = parent_pos(rpos); parent->BlitBack(z_pos, ppos); } if (is_hidden_now) return; self_redraw: 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) { iterator end = children.end(); for (iterator it = children.begin(); 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); } } } 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 (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); } root->BlitSurface(surface_own, rpos, surface_alpha, surface_alpha_rect, root->surface, apos, attribute); } else if (parent == NULL) { // 親がいないなら背景消去の責任をもつ 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 != NULL) { 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 = NULL; } PicWidget::~PicWidget() { if (pic != NULL) { pic->SetEventWidget(0); delete pic; } pic = NULL; } void PicWidget::SetPic(PicBase* new_pic) { if (pic != NULL) { pic->SetEventWidget(0); delete pic; } pic = new_pic; if (pic != NULL) pic->SetEventWidget(this); } PicBase* PicWidget::Pic(void) { if (pic == NULL) { fprintf(stderr,"Error: PicWidget::Pic returns zero.\n"); } return pic; } PicContainer* PicWidget::PicNode(void) { PicContainer* node = dynamic_cast<PicContainer*>(pic); if (node == NULL) { 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 { private: 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 = FileSearcher::GetInstance()->Find(FileSearcher::PDT, name.c_str(), "pdt"); if (info == NULL) return NULL; GRPCONV* conv = GRPCONV::AssignConverter(info); if (conv == NULL) { delete info; return NULL; } mem = (char*)malloc(conv->Width() * conv->Height() * 4 + 1024); Surface* s = NULL; 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 == NULL) return NULL; 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 = NULL; 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(); 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; } } } } 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; item.pic->ExecReBlit(item.rpos); 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++; } 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 == NULL) return NULL; Surface* s = ftosurface->Load(f); if (s == NULL) return NULL; 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 { SDL_Rect sr = SDLed(src_r); SDL_Rect dr = SDLed(dest_r); if (attribute & PicBase::BLIT_MULTIPLY) { DSurfaceBlitMultiply(src, src_r, dest, dest_r); return; } else if (attribute & PicBase::BLIT_ADD) { unsigned char a = 255; if (alpha != NULL && alpha_r.width() >= 1 && alpha_r.height() >= 1) a = *alpha; DSurfaceBlitAdd(src, src_r, dest, dest_r, a); return; } if (alpha == NULL || alpha_r.width() == 0) // simple blit { SDL_BlitSurface(src, &sr, dest, &dr); return; } if (alpha_r.width() == 1 && alpha_r.height() == 1) { if (*alpha == 255) { SDL_BlitSurface(src, &sr, dest, &dr); return; } if (src->format->Amask == 0) { // use per-surface alpha SDL_SetAlpha(src, SDL_SRCALPHA, *alpha); SDL_BlitSurface(src, &sr, dest, &dr); SDL_SetAlpha(src, 0, 0); return; } } // generic alpha blit DSurfaceBlitAlpha(src, src_r, dest, dest_r, alpha, alpha_r); } 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); } }