OSDN Git Service

updates
authorshy <shy>
Tue, 10 Apr 2007 14:38:59 +0000 (14:38 +0000)
committershy <shy>
Tue, 10 Apr 2007 14:38:59 +0000 (14:38 +0000)
12 files changed:
ChangeLog
lib/ninix/balloon.py
lib/ninix/dll/bln.py
lib/ninix/dll/hanayu.py
lib/ninix/kinoko.py
lib/ninix/main.py
lib/ninix/menu.py
lib/ninix/nekodorif.py
lib/ninix/ngm.py
lib/ninix/pix.py
lib/ninix/seriko.py
lib/ninix/surface.py

index dfb515e..a5bde8e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+Tue April 10 2007   Shyouzou Sugitani <shy@users.sourceforge.jp>
+       * gdk-pixbufを使用している部分のメモリリーク対策をした.
+       * gdk-pixbuf関連のコードをSerikoからSurfaceクラスに移した.
+
 Sat March 24 2007   Shyouzou Sugitani <shy@users.sourceforge.jp>
        * メニューアイコンの管理を各ゴーストからApplicationクラスに移した.
 
index 0928974..33a6dc4 100644 (file)
@@ -14,6 +14,7 @@
 #
 
 import os
+import gc
 
 if 'DISPLAY' in os.environ:
     import gtk
@@ -434,6 +435,8 @@ class BalloonWindow:
         self.clear_text()
 
     def reset_pixbuf_cache(self):
+        self.pixbuf = None
+        gc.collect()
         self.pixbuf = {}
         for key, (path, config) in self.balloon.iteritems():
             try:
@@ -1228,6 +1231,8 @@ class BalloonWindow:
         w = pixbuf.get_width()
         h = pixbuf.get_height()
         self.images.append((pixbuf, (w, h), (x, y)))
+        del pixbuf
+        gc.collect()
         self.redraw()
 
     def draw_last_line(self, column=0):
index f4d806e..8c8a79f 100644 (file)
@@ -20,6 +20,7 @@ import random
 import math
 import time
 import sys
+import gc
 
 if 'DISPLAY' in os.environ:
     if 'gtk' not in sys.modules:
@@ -239,6 +240,8 @@ class Balloon:
             balloon_pixbuf = balloon_pixbuf.scale_simple(
                 w, h, gtk.gdk.INTERP_BILINEAR)
         balloon_pixmap, mask_pixmap = balloon_pixbuf.render_pixmap_and_mask(1)
+        del balloon_pixbuf
+        gc.collect()
         if self.position == 'lefttop':
             pass
         elif self.position == 'leftbottom':
index 724aae4..f1f61c3 100644 (file)
@@ -11,7 +11,7 @@
 #  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 #  PURPOSE.  See the GNU General Public License for more details.
 #
-#  $Id: hanayu.py,v 1.32 2006/03/25 08:07:09 shy Exp $
+#  $Id: hanayu.py,v 1.33 2007/04/10 14:38:59 shy Exp $
 #
 
 # TODO:
@@ -22,6 +22,7 @@ import sys
 import os
 import math
 import time
+import gc
 
 if 'DISPLAY' in os.environ:
     if 'gtk' not in sys.modules:
@@ -302,6 +303,8 @@ class Graph:
                                             0, 0, xoffset, yoffset,
                                             width, height,
                                             gtk.gdk.RGB_DITHER_NONE, 0, 0)
+            del pixbuf
+            gc.collect()
         self.darea.window.draw_drawable(self.surface_gc, self.surface_pixmap,
                                         0, 0, 0, 0, self.width, self.height)
 
index 3057201..9664c1e 100644 (file)
@@ -16,6 +16,7 @@
 # - 透明度の設定
 
 import os
+import gc
 
 if 'DISPLAY' in os.environ:
     import gtk
@@ -190,6 +191,7 @@ class Kinoko:
 class Skin:
 
     def __init__(self, data, accelgroup, kinoko, scale):
+        self.frame_buffer = []
         self.data = data
         self.accelgroup = accelgroup
         self.kinoko = kinoko
@@ -237,6 +239,8 @@ class Skin:
                 surface_pixbuf = self.pixbuf.scale_simple(
                     w, h, gtk.gdk.INTERP_BILINEAR)
             image, mask = surface_pixbuf.render_pixmap_and_mask(255)
+            del surface_pixbuf
+            gc.collect()
         except: ## FIXME
             self.kinoko.close(None)
             return
@@ -317,9 +321,52 @@ class Skin:
             surface_pixbuf = self.pixbuf.copy()
         return surface_pixbuf
 
-    def update(self, cache=None):
-        if cache is not None:
-            surface_pixbuf = cache
+    def clear_frame_buffer(self):
+        self.frame_buffer = []
+
+    def update_frame_buffer(self, seriko, frame, move, dirty):
+        if dirty:
+            surface_pixbuf = self.create_surface_pixbuf(seriko.base_id)
+            if surface_pixbuf is not None:
+                # draw overlays
+                for pixbuf_id, x, y in seriko.iter_overlays():
+                    try:
+                        pixbuf = self.get_pixbuf(pixbuf_id)
+                        w = pixbuf.get_width()
+                        h = pixbuf.get_height()
+                    except:
+                        continue
+                    # overlay surface pixbuf
+                    sw = surface_pixbuf.get_width()
+                    sh = surface_pixbuf.get_height()
+                    if x + w > sw:
+                        w = sw - x
+                    if y + h > sh:
+                        h = sh - y
+                    if x < 0:
+                        dest_x = 0
+                        w += x
+                    else:
+                        dest_x = x
+                    if y < 0:
+                        dest_y = 0
+                        h += y
+                    else:
+                        dest_y = y
+                    pixbuf.composite(surface_pixbuf, dest_x, dest_y,
+                                     w, h, x, y, 1.0, 1.0,
+                                     gtk.gdk.INTERP_BILINEAR, 255)
+        else:
+            surface_pixbuf = None
+        self.frame_buffer.append((frame, move, surface_pixbuf, dirty))
+
+    def update(self):
+        if len(self.frame_buffer) > 0:
+            frame, move, surface_pixbuf, dirty = self.frame_buffer.pop(0)
+            if move is not None:
+                self.move_surface(*move)
+            if not dirty:
+                return
         else:
             surface_pixbuf = self.create_surface_pixbuf()
         if self.__scale != 100:
@@ -328,6 +375,8 @@ class Skin:
             surface_pixbuf = surface_pixbuf.scale_simple(
                 w, h, gtk.gdk.INTERP_BILINEAR)
         image, mask = surface_pixbuf.render_pixmap_and_mask(255)
+        del surface_pixbuf
+        gc.collect()
         self.w, self.h = self.surface_pixmap.get_size()
         self.darea.window.set_back_pixmap(image, False)
         self.window.shape_combine_mask(mask, 0, 0)
@@ -336,10 +385,10 @@ class Skin:
         self.darea.queue_draw()
 
     def terminate(self):
-        self.seriko.terminate()
+        self.seriko.terminate(self)
 
     def add_overlay(self, actor, surface_id, x, y):
-        self.seriko.add_overlay(actor, surface_id, x, y)
+        self.seriko.add_overlay(self, actor, surface_id, x, y)
 
     def remove_overlay(self, actor):
         self.seriko.remove_overlay(actor)
@@ -358,7 +407,7 @@ class Skin:
         self.kinoko.close(None)
 
     def destroy(self): ## FIXME
-        self.seriko.terminate()
+        self.seriko.terminate(self)
         self.window.destroy()
 
     def button_press(self, widget, event): ## FIXME
index ebd00f2..d5e7096 100755 (executable)
@@ -13,7 +13,7 @@
 #  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 #  PURPOSE.  See the GNU General Public License for more details.
 #
-# $Id: main.py,v 1.139 2007/03/24 03:51:08 shy Exp $
+# $Id: main.py,v 1.140 2007/04/10 14:38:59 shy Exp $
 #
 
 import getopt
@@ -26,6 +26,7 @@ import gettext
 import traceback
 import cStringIO
 import fcntl
+import gc
 
 gettext.install('ninix')
 
@@ -567,6 +568,8 @@ class Application:
                 pixbuf = None
             if pixbuf is not None:
                 icon = pixbuf.scale_simple(16, 16, gtk.gdk.INTERP_BILINEAR)
+                del pixbuf
+                gc.collect()
             else:
                 icon = None
         else:
index 2065460..23ddfa7 100644 (file)
@@ -13,6 +13,7 @@
 
 import os
 import re
+import gc
 
 if 'DISPLAY' in os.environ:
     import gtk
@@ -258,6 +259,9 @@ class Menu:
                 1.0, 1.0, gtk.gdk.INTERP_BILINEAR, 255)
             self.__sidebar_width = sidebar_width
             pixmap, mask = new_pixbuf.render_pixmap_and_mask(255)
+            del pixbuf
+            del new_pixbuf
+            gc.collect()
         else:
             self.__sidebar_width = 0
         self.__pixmap_with_sidebar = pixmap
@@ -297,6 +301,10 @@ class Menu:
                 0, 0,
                 1.0, 1.0, gtk.gdk.INTERP_BILINEAR, 255)
             pixmap, mask = new_pixbuf.render_pixmap_and_mask(255)
+            del pixbuf
+            del new_pixbuf
+            del pixbuf_sidebar
+            gc.collect()
         self.__pixmap_prelight_with_sidebar = pixmap
 
     def __set_mayuna_menu(self, side):
index 387c444..7d23d04 100644 (file)
@@ -48,6 +48,7 @@
 
 import os
 import random
+import gc
 
 if 'DISPLAY' in os.environ:
     import gtk
@@ -330,6 +331,8 @@ class Skin:
                 h = max(1, pixbuf.get_height() * self.__scale / 100)
                 pixbuf = pixbuf.scale_simple(w, h, gtk.gdk.INTERP_BILINEAR)
             image, mask = pixbuf.render_pixmap_and_mask(255)
+            del pixbuf
+            gc.collect()
         except:
             self.nekoninni.finalize()
             return
@@ -522,6 +525,8 @@ class Katochan:
                 h = pixbuf.get_height() * self.__scale / 100
                 pixbuf = pixbuf.scale_simple(w, h, gtk.gdk.INTERP_BILINEAR)
             image, mask = pixbuf.render_pixmap_and_mask(255)
+            del pixbuf
+            gc.collect()
         except:
             self.nekoninni.finalize()
             return
index da39c11..ed73ba3 100644 (file)
@@ -17,6 +17,7 @@ import re
 import sys
 import urllib
 import time
+import gc
 
 from xml.dom import pulldom
 
@@ -497,6 +498,8 @@ class UI:
                 try:
                     pixbuf = ninix.pix.create_pixbuf_from_file(filename)
                     image, mask = pixbuf.render_pixmap_and_mask(255)
+                    del pixbuf
+                    gc.collect()
                 except:
                     darea.window.set_back_pixmap(None, False)
                 else:
index 66529f5..180ccab 100644 (file)
@@ -13,6 +13,7 @@
 import sys
 import os
 import md5
+import gc
 
 if 'DISPLAY' in os.environ:
     import gtk
@@ -88,16 +89,6 @@ def create_pixbuf_from_DDP_file(path):
     loader.close()
     return pixbuf
 
-def add_alpha_from_PNA_file(pixbuf, path):
-    assert pixbuf.get_has_alpha()
-    pna_pixbuf = gtk.gdk.pixbuf_new_from_file(path)
-    pna_array = pna_pixbuf.get_pixels_array()
-    assert pna_pixbuf.get_bits_per_sample() / 8 == 1 ## FIXME
-    pixbuf_array = pixbuf.get_pixels_array()
-    width = pixbuf.get_width()
-    height = pixbuf.get_height()
-    pixbuf_array[:,:,3] = pna_array[:,:,0]
-
 def create_pixbuf_from_file(path, is_pnr=1, use_pna=0):
     head, tail = os.path.split(path)
     basename, suffix = os.path.splitext(tail)
@@ -127,27 +118,19 @@ def create_pixbuf_from_file(path, is_pnr=1, use_pna=0):
     if use_pna:
         path = os.path.join(head, ''.join((basename, '.pna')))
         if os.path.exists(path):
-            add_alpha_from_PNA_file(pixbuf, path)
+            assert pixbuf.get_has_alpha()
+            pna_pixbuf = gtk.gdk.pixbuf_new_from_file(path)
+            pna_array = pna_pixbuf.get_pixels_array()
+            assert pna_pixbuf.get_bits_per_sample() / 8 == 1 ## FIXME
+            pixbuf_array = pixbuf.get_pixels_array()
+            pixbuf_array[:,:,3] = pna_array[:,:,0]
+            del pna_pixbuf
+            gc.collect()
     return pixbuf
 
 def create_pixmap_from_file(path):
     pixbuf = create_pixbuf_from_file(path)
     pixmap, mask = pixbuf.render_pixmap_and_mask(1)
+    del pixbuf
+    gc.collect()
     return pixmap, mask
-
-def reduce_pixbuf(target_pixbuf, pixbuf, dest_x, dest_y):
-    if pixbuf.get_has_alpha():
-        dest_w = target_pixbuf.get_width()
-        dest_h = target_pixbuf.get_height()
-        target_array = target_pixbuf.get_pixels_array()
-        array = pixbuf.get_pixels_array()
-        w = pixbuf.get_width()
-        h = pixbuf.get_height()
-        for i in range(h):
-            for j in range(w):
-                if array[i][j][3] == 0: # alpha
-                    x = j + dest_x
-                    y = i + dest_y
-                    if 0 <= x < dest_w and 0 <= y < dest_h:
-                        target_array[y][x][3] = 0 # alpha
-    return target_pixbuf
index b1d9de3..94d459d 100644 (file)
@@ -11,7 +11,7 @@
 #  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 #  PURPOSE.  See the GNU General Public License for more details.
 #
-# $Id: seriko.py,v 1.25 2006/10/05 15:16:44 shy Exp $
+# $Id: seriko.py,v 1.26 2007/04/10 14:38:59 shy Exp $
 #
 
 import re
@@ -38,23 +38,12 @@ class Controler:
         self.frame = {'step': 0.033, # [sec]
                       'next': 0}
         self.active = []
-        self.__cache = []
         self.__move = None
         self.__dirty = 1
 
-    def cache_is_empty(self):
-        if self.__cache:
-            return False
-        else:
-            return True
-
-    def get_cache(self):
-        assert self.__cache is not None
-        return self.__cache[0][2]
-
     def set_base_id(self, window, surface_id):
         if surface_id == -2:
-            self.terminate()
+            self.terminate(window)
             self.base_id = window.surface_id
         else:
             self.base_id = surface_id
@@ -63,39 +52,6 @@ class Controler:
     def move_surface(self, xoffset, yoffset):
         self.__move = (xoffset, yoffset)
 
-    def draw_overlays(self, window, surface_pixbuf):
-        ##print 'draw_overlays()'
-        if surface_pixbuf is None:
-            return None
-        for pixbuf_id, x, y in self.iter_overlays():
-            try:
-                pixbuf = window.get_pixbuf(pixbuf_id)
-                w = pixbuf.get_width()
-                h = pixbuf.get_height()
-            except:
-                continue
-            # overlay surface pixbuf
-            sw = surface_pixbuf.get_width()
-            sh = surface_pixbuf.get_height()
-            if x + w > sw:
-                w = sw - x
-            if y + h > sh:
-                h = sh - y
-            if x < 0:
-                dest_x = 0
-                w += x
-            else:
-                dest_x = x
-            if y < 0:
-                dest_y = 0
-                h += y
-            else:
-                dest_y = y
-            pixbuf.composite(surface_pixbuf, dest_x, dest_y,
-                             w, h, x, y, 1.0, 1.0,
-                             gtk.gdk.INTERP_BILINEAR, 255)
-        return surface_pixbuf
-
     def append_actor(self, frame, actor):
         self.active.append((frame, actor))
 
@@ -112,7 +68,7 @@ class Controler:
     def update_frame(self, window):
         if self.terminate_flag:
             return False
-        if len(self.__cache) > 1.0 / self.frame['step']: # XXX
+        if len(window.frame_buffer) > 1.0 / self.frame['step']: # XXX
             return True
         self.frame['next'] += self.frame['step']
         frame, actor = self.get_actor_next(window)
@@ -121,7 +77,7 @@ class Controler:
             actor.update(window, frame)
             last_actor = actor
             frame, actor = self.get_actor_next(window)
-        self.update_surface_cache(window)
+        self.update_surface_frame_buffer(window)
         if last_actor is not None and last_actor.exclusive and \
            last_actor.terminate_flag and self.exclusive_actor is None: # XXX
             self.invoke_restart(window)
@@ -135,30 +91,21 @@ class Controler:
                 return self.active.pop(0)
         return None, None
 
-    def update_surface_cache(self, window):
-        if self.__dirty:
-            surface_pixbuf = window.create_surface_pixbuf(self.base_id)
-            surface_pixbuf = self.draw_overlays(window, surface_pixbuf)
-        else:
-            surface_pixbuf = None
-        self.__cache.append((self.frame['next'], self.__move, surface_pixbuf, self.__dirty))
+    def update_surface_frame_buffer(self, window):
+        window.update_frame_buffer(
+            self, self.frame['next'], self.__move, self.__dirty)
         self.__dirty = 0
 
     def update(self, window):
         if self.terminate_flag:
             return False
-        if self.__cache:
-            frame, move, surface, dirty = self.__cache.pop(0)
-            if move is not None:
-                window.move_surface(*move)
-            if dirty:
-                window.update(surface)
+        window.update()
         self.timeout_id = gobject.timeout_add(int(self.frame['step'] * 1000), self.update, window)
         return False
 
     def lock_exclusive(self, window, actor):
         assert self.exclusive_actor is None
-        self.terminate()
+        self.terminate(window)
         self.exclusive_actor = actor
         actor.set_post_proc(self.unlock_exclusive, (window, actor))
         self.update_frame(window) # XXX
@@ -179,9 +126,9 @@ class Controler:
             pass
         self.__dirty = 1
 
-    def add_overlay(self, actor, pixbuf_id, x, y):
+    def add_overlay(self, window, actor, pixbuf_id, x, y):
         if pixbuf_id == '-2':
-            self.terminate()
+            self.terminate(window)
         if pixbuf_id in ['-1', '-2']:
             self.remove_overlay(actor)
             return
@@ -264,7 +211,7 @@ class Controler:
 
     def reset(self, window, surface_id):
         self.queue = []
-        self.terminate()
+        self.terminate(window)
         self.frame['next'] = 0
         self.set_base_id(window, window.surface_id)
         if surface_id in self.seriko:
@@ -286,7 +233,7 @@ class Controler:
         self.invoke_always(window)
         self.timeout_id = gobject.timeout_add(int(self.frame['step'] * 1000), self.update, window)
 
-    def terminate(self):
+    def terminate(self, window):
         self.terminate_flag = 1
         if self.timeout_id is not None:
             gobject.source_remove(self.timeout_id)
@@ -301,7 +248,7 @@ class Controler:
         self.active = []
         self.__move = None
         self.__dirty = 1
-        self.__cache = []
+        window.clear_frame_buffer()
         self.terminate_flag = 0
 
     def iter_overlays(self):
index 86a7698..10759f8 100644 (file)
@@ -17,6 +17,7 @@ import os
 import re
 import urlparse
 import random
+import gc
 
 if 'DISPLAY' in os.environ:
     import gtk
@@ -452,6 +453,8 @@ class Surface:
                     key, method, filename, x, y, w, h)
         if error is not None:
             print error
+            pixbuf_list = None
+            gc.collect()
             pixbuf_list = []
         return pixbuf_list
 
@@ -681,6 +684,8 @@ class Surface:
                 pixbuf = None
         for window in self.window:
             window.window.set_icon(pixbuf)
+        del pixbuf
+        gc.collect()
 
     def __check_mikire_kasanari(self):
         if not self.is_shown(0):
@@ -824,6 +829,7 @@ class SurfaceWindow:
         self.mayuna = mayuna
         self.bind = bind
         self.reset_pixbuf_cache()
+        self.frame_buffer = []
         self.default_id = default_id
         self.debug = debug
         self.__shown = 0
@@ -924,7 +930,7 @@ class SurfaceWindow:
             if aliases:
                 surface_id = random.choice(aliases)
         if surface_id == '-2':
-            self.seriko.terminate()
+            self.seriko.terminate(self)
         if surface_id in ['-1', '-2']:
             pass
         elif surface_id not in self.pixbuf:
@@ -958,9 +964,8 @@ class SurfaceWindow:
         self.set_position(x, y)
         self.sakura.ghost.notify_surface_change(self.side) ## FIXME
 
-    def compose_surface(self, surface_pixbuf, mayuna, done):
-        for pattern in mayuna.patterns:
-            surface, interval, method, args = pattern
+    def iter_mayuna(self, surface_width, surface_height, mayuna, done):
+        for surface, interval, method, args in mayuna.patterns:
             if method in ['bind', 'add']:
                 if surface in self.pixbuf:
                     x, y = args
@@ -968,12 +973,10 @@ class SurfaceWindow:
                     w = pixbuf.get_width()
                     h = pixbuf.get_height()
                     # overlay surface pixbuf
-                    sw = surface_pixbuf.get_width()
-                    sh = surface_pixbuf.get_height()
-                    if x + w > sw:
-                        w = sw - x
-                    if y + h > sh:
-                        h = sh - y
+                    if x + w > surface_width:
+                        w = surface_width - x
+                    if y + h > surface_height:
+                        h = surface_height - y
                     if x < 0:
                         dest_x = 0
                         w += x
@@ -984,17 +987,15 @@ class SurfaceWindow:
                         h += y
                     else:
                         dest_y = y
-                    pixbuf.composite(surface_pixbuf, dest_x, dest_y,
-                                     w, h, x, y, 1.0, 1.0,
-                                     gtk.gdk.INTERP_BILINEAR, 255)
+                    yield method, pixbuf, dest_x, dest_y, w, h, x, y
             elif method == 'reduce':
                 if surface in self.pixbuf:
-                    x, y = args
+                    dest_x, dest_y = args
                     pixbuf = self.get_pixbuf(surface)
                     w = pixbuf.get_width()
                     h = pixbuf.get_height()
-                    surface_pixbuf = pix.reduce_pixbuf(
-                        surface_pixbuf, pixbuf, x, y)
+                    x = y = 0 # XXX
+                    yield method, pixbuf, dest_x, dest_y, w, h, x, y
             elif method == 'insert':
                 index = args[0]
                 for actor in self.mayuna[self.surface_id]:
@@ -1003,15 +1004,17 @@ class SurfaceWindow:
                         if actor_id in self.bind and self.bind[actor_id][1] and \
                            actor_id not in done:
                             done.append(actor_id)
-                            surface_pixbuf = self.compose_surface(
-                                surface_pixbuf, actor, done)
+                            for result in self.iter_mayuna(surface_width, surface_height, actor, done):
+                                yield result
                         else:
                             break
             else:
                 raise RuntimeError, 'should not reach here'
-        return surface_pixbuf
 
     def reset_pixbuf_cache(self):
+        self.pixbuf_cache = None
+        self.mayuna_cache = None
+        gc.collect()
         self.pixbuf_cache = {}
         self.mayuna_cache = {}
 
@@ -1067,6 +1070,9 @@ class SurfaceWindow:
             overlay.composite(pixbuf, dest_x, dest_y,
                               w, h, x, y, 1.0, 1.0,
                               gtk.gdk.INTERP_BILINEAR, 255)
+            del overlay
+        ##else:
+        ##    gc.collect() # XXX
         return pixbuf
 
     def get_pixbuf(self, pixbuf_id):
@@ -1106,16 +1112,83 @@ class SurfaceWindow:
                     if actor_id in self.bind and self.bind[actor_id][1] and \
                        actor_id not in done:
                         done.append(actor_id)
-                        surface_pixbuf = self.compose_surface(
-                            surface_pixbuf, actor, done)
+                        #surface_pixbuf = self.compose_surface(
+                        #    surface_pixbuf, actor, done)
+                        surface_width = surface_pixbuf.get_width()
+                        surface_height = surface_pixbuf.get_height()
+                        for method, pixbuf, dest_x, dest_y, w, h, x, y in self.iter_mayuna(surface_width, surface_height, actor, done):
+                            if method in ['bind', 'add']:
+                                pixbuf.composite(surface_pixbuf, dest_x, dest_y,
+                                                 w, h, x, y, 1.0, 1.0,
+                                                 gtk.gdk.INTERP_BILINEAR, 255)
+                            elif method == 'reduce':
+                                if pixbuf.get_has_alpha():
+                                    dest_w = surface_width
+                                    dest_h = surface_height
+                                    surface_array = surface_pixbuf.get_pixels_array()
+                                    array = pixbuf.get_pixels_array()
+                                    for i in range(h):
+                                        for j in range(w):
+                                            if array[i][j][3] == 0: # alpha
+                                                x = j + dest_x
+                                                y = i + dest_y
+                                                if 0 <= x < dest_w and 0 <= y < dest_h:
+                                                    surface_array[y][x][3] = 0 # alpha
+                            else:
+                                raise RuntimeError, 'should not reach here'
                 self.mayuna_cache[surface_id] = surface_pixbuf.copy()
         else:
             surface_pixbuf =self.get_pixbuf(surface_id)
         return surface_pixbuf
 
-    def update(self, cache=None):
-        if cache is not None:
-            surface_pixbuf = cache
+    def clear_frame_buffer(self):
+        self.frame_buffer = []
+
+    def update_frame_buffer(self, seriko, frame, move, dirty):
+        if dirty:
+            surface_pixbuf = self.create_surface_pixbuf(seriko.base_id)
+            if surface_pixbuf is not None:
+                # draw overlays
+                for pixbuf_id, x, y in seriko.iter_overlays():
+                    try:
+                        pixbuf = self.get_pixbuf(pixbuf_id)
+                        w = pixbuf.get_width()
+                        h = pixbuf.get_height()
+                    except:
+                        continue
+                    # overlay surface pixbuf
+                    sw = surface_pixbuf.get_width()
+                    sh = surface_pixbuf.get_height()
+                    if x + w > sw:
+                        w = sw - x
+                    if y + h > sh:
+                        h = sh - y
+                    if x < 0:
+                        dest_x = 0
+                        w += x
+                    else:
+                        dest_x = x
+                    if y < 0:
+                        dest_y = 0
+                        h += y
+                    else:
+                        dest_y = y
+                    pixbuf.composite(surface_pixbuf, dest_x, dest_y,
+                                     w, h, x, y, 1.0, 1.0,
+                                     gtk.gdk.INTERP_BILINEAR, 255)
+                    del pixbuf
+                ##gc.collect() # XXX
+        else:
+            surface_pixbuf = None
+        self.frame_buffer.append((frame, move, surface_pixbuf, dirty))
+
+    def update(self):
+        if len(self.frame_buffer) > 0:
+            frame, move, surface_pixbuf, dirty = self.frame_buffer.pop(0)
+            if move is not None:
+                self.move_surface(*move)
+            if not dirty:
+                return
         else:
             surface_pixbuf = self.create_surface_pixbuf()
         if self.__scale != 100:
@@ -1142,7 +1215,7 @@ class SurfaceWindow:
         self.seriko.remove_overlay(actor)
 
     def add_overlay(self, actor, pixbuf_id, x, y):
-        self.seriko.add_overlay(actor, pixbuf_id, x, y)
+        self.seriko.add_overlay(self, actor, pixbuf_id, x, y)
 
     def __move(self, xoffset=0, yoffset=0):
         x, y = self.get_position()
@@ -1236,7 +1309,7 @@ class SurfaceWindow:
         self.reset_pixbuf_cache()
         for tag in self.callbacks:
             self.darea.disconnect(tag)
-        self.seriko.terminate()
+        self.seriko.terminate(self)
         self.window.remove(self.darea)
         self.darea.destroy()
         self.window.destroy()