changeset 906:62ef69e02e85

branch merge
author Shih-Yuan Lee (FourDollars) <fourdollars@gmail.com>
date Sun, 03 Oct 2010 18:15:00 +0800
parents e3a5e05f00c1 (current diff) e86b4d56ddea (diff)
children 095cf170f5fd
files
diffstat 13 files changed, 1113 insertions(+), 413 deletions(-) [+]
line wrap: on
line diff
--- a/include/mb_types.h	Sun Oct 03 18:12:11 2010 +0800
+++ b/include/mb_types.h	Sun Oct 03 18:15:00 2010 +0800
@@ -82,7 +82,7 @@
 struct _paint {
     int pnt_type;
     int flags;
-    void (*prepare)(paint_t *paint, mbe_t *cr);
+    void (*prepare)(paint_t *paint, mbe_t *cr, shape_t *sh);
     void (*free)(struct _redraw_man *rdman, paint_t *paint);
     STAILQ(shnode_t) members;
     paint_t *pnt_next;		/*!< \brief Collect all paints of a rdman. */
@@ -175,6 +175,12 @@
                                  *   cached. */
     area_t *pcache_cur_area;	/*!< Current area for parent cached. */
     area_t *pcache_last_area;	/*!< Last area for parent cached. */
+    co_aix cache_2_pdev[6];	/*!< Transfrom matrix from space of
+				 * cached one to its parent. */
+    co_aix cache_2_pdev_rev[6];	/*!< Reverse of cache_2_pdev. */
+    co_aix aggr_2_pdev[6];	/*!< Aggregation of cache_2_pdev from root  */
+    co_aix aggr_2_pdev_rev[6];	/*!< Aggregation of cache_2_pdev_rev
+				 * from root  */
 } coord_canvas_info_t;
 
 /*! \brief A coordination system.
@@ -367,6 +373,10 @@
 #define _coord_get_dirty_areas(coord) (&(coord)->canvas_info->dirty_areas)
 #define _coord_get_aggr_dirty_areas(coord)	\
     ((coord)->canvas_info->aggr_dirty_areas)
+#define coord_get_2pdev(coord) ((coord)->canvas_info->cache_2_pdev)
+#define coord_get_2pdev_rev(coord) ((coord)->canvas_info->cache_2_pdev_rev)
+#define coord_get_aggr2pdev(coord) ((coord)->canvas_info->aggr_2_pdev)
+#define coord_get_aggr2pdev_rev(coord) ((coord)->canvas_info->aggr_2_pdev_rev)
 
 /* @} */
 
--- a/nodejs/animate.js	Sun Oct 03 18:12:11 2010 +0800
+++ b/nodejs/animate.js	Sun Oct 03 18:15:00 2010 +0800
@@ -9,84 +9,50 @@
 var ffs = 12;
 var frame_interval = 1000 / ffs;
 
-function linear_draw() {
-    var percent;
+function shift_draw(percent) {
+    var x, y;
     
-    percent = (Date.now() - this._start_tm) / this.duration;
-    if(percent >= 1) {
-	percent = 1;
-        if (this.obj.timer) {
-	    this.obj.timer.stop();
-	    delete this.obj.timer;
-	}
-    }
-    this.obj.x = (this.targetx-this.startposx)*percent+this.startposx;
-    this.obj.y = (this.targety-this.startposy)*percent+this.startposy;
+    x = (this.targetx - this.startposx) * percent + this.startposx;
+    y = (this.targety - this.startposy) * percent + this.startposy;
+    this.obj.center.move(x, y);
     this.app.refresh();
 }
 
-function linear_draw_start() {
-    var obj = this.obj;
-    var self = this;
-    
-    if(obj.timer)
-	obj.timer.stop();
-
-    this._start_tm = Date.now();
-    obj.timer = setInterval(function() { self.draw(); }, frame_interval);
-}
-
-function linear(app,obj,shiftx,shifty,duration) {
-    obj.animated_linear = this;
+function shift(app,obj,shiftx,shifty) {
+    obj.animated_shift = this;
     this.app = app;
     this.obj = obj;
     this.end = 0;
-    this.targetx = shiftx + obj.x;
-    this.targety = shifty + obj.y;
-    this.startposx = obj.x;
-    this.startposy = obj.y;
-    this.duration = duration*1000;
+    this.targetx = shiftx + obj.center.x;
+    this.targety = shifty + obj.center.y;
+    this.startposx = obj.center.x;
+    this.startposy = obj.center.y;
 }
 
-exports.linear = linear;
-linear.prototype.start = linear_draw_start;
-linear.prototype.draw = linear_draw;
+exports.shift = shift;
+shift.prototype.draw = shift_draw;
 
 /* ------------------------------------------------------------ */
 function rotate(app, obj, ang, duration) {
     this._app = app;
     this._obj = obj;
     this._ang = ang;
-    this._duration = duration * 1000;
+    this._start_mtx = [obj[0], obj[1], obj[2], obj[3], obj[4], obj[5]];
 }
 
-function rotate_start() {
-    var obj = this._obj;
-    var self = this;
-    
-    this._start_mtx = [obj[0], obj[1], obj[2], obj[3], obj[4], obj[5]];
-    this._start_tm = Date.now();
-    obj.timer = setInterval(function() { self.draw(); }, frame_interval);
-}
-
-function rotate_draw() {
+function rotate_draw(percent) {
     var percent;
     var ang;
     var sv, cv;
     var obj = this._obj;
     var mtx, shift;
 
-    percent = (Date.now() - this._start_tm) / this._duration;
-    if(percent >= 1) {
-	percent = 1;
-	obj.timer.stop();
-    }
 
     ang = percent * this._ang;
     sv = Math.sin(ang);
     cv = Math.cos(ang);
     mtx = [cv, -sv, 0, sv, cv, 0];
-
+    sys.puts('x='+obj.center.x+',y='+obj.center.y);
     shift = [1, 0, -obj.center.x, 0, 1, -obj.center.y];
     mtx = multiply(mtx, shift);
     shift = [1, 0, obj.center.x, 0, 1, obj.center.y];
@@ -103,7 +69,6 @@
     this._app.refresh();
 }
 
-rotate.prototype.start = rotate_start;
 rotate.prototype.draw = rotate_draw;
 exports.rotate = rotate;
 
@@ -119,59 +84,56 @@
 }
 
 
-function scale_draw() {
-    if (this.end == 1) return;
-    var percent = (Date.now() - this.starttime)/this.duration;
-    if (percent > 1) percent = 1;
-    var sx = (this.targetx-this.startsx)*percent+this.startsx;
-    var sy = (this.targety-this.startsy)*percent+this.startsy;
-    var t=[sx,0,0,0,sy,0];
-    this.obj[0] = sx;
-    this.obj[4] = sy;
-    this.obj[2] = this.origin_offset_x - (sx-this.startsx)*this.obj.center.x;
-    this.obj[5] = this.origin_offset_y - (sy-this.startsy)*this.obj.center.y;
+function scale_draw(percent) {
+    var sx = 1 + (this.totalsx - 1) * percent;
+    var sy = 1 + (this.totalsy - 1) * percent;
+    var sh1 = [1, 0, -this.center_x, 0, 1, -this.center_y];
+    var sh2 = [1, 0, this.center_x, 0, 1, this.center_y];
+    var scale = [sx, 0, 0, 0, sy, 0];
+    var obj = this.obj;
+    var mtx;
+
+    mtx = multiply(scale, sh1);
+    mtx = multiply(sh2, mtx);
+    mtx = multiply(this.orig_mtx, mtx);
+    obj[0] = mtx[0];
+    obj[1] = mtx[1];
+    obj[2] = mtx[2];
+    obj[3] = mtx[3];
+    obj[4] = mtx[4];
+    obj[5] = mtx[5];
 
     this.app.refresh();
-    var self = this;
-    if (percent < 1) {
-	this.obj.timer=setTimeout(function() { self.draw();}, frame_interval);
-	return;
-    }
-    this.app.refresh();
-    this.obj.animated_scale = null;
 }
 
-function scale(app,obj,targetx,targety, duration) {
+function scale(app, obj, fact_x, fact_y, duration) {
+    var bbox;
+    
     try {
         if (obj.animated_scale) {
-	    //obj[0] = obj.animated_scale.targetx;
-	    //obj[4] = obj.animated_scale.targety;
-	    //obj[2] = obj.animated_scale.final_offset_x;
-	    //obj[5] = obj.aninated_scale.final_offset_y;
 	    obj.animated_scale.end = 1;
 	}
     } catch(e) {
 	    
     }
+
+    bbox = obj.bbox;
+    bbox.update();
     obj.animated_scale = this;
     this.app = app;
     this.obj = obj;
     this.end = 0;
     this.starttime = Date.now();
-    this.startsx = obj[0];
-    this.startsy = obj[4];
-    this.targetx = targetx;
-    this.targety = targety;
+    this.totalsx = fact_x * bbox.orig.width / bbox.width;
+    this.totalsy = fact_y * bbox.orig.height / bbox.height;
     this.duration = duration*1000;
-    this.origin_offset_x = obj[2];
-    this.origin_offset_y = obj[5];
-    this.final_offset_x = this.origin_offset_x-(targetx-this.startsx)*obj.center.x;
-    this.final_offset_y = this.origin_offset_y-(targety-this.startsy)*obj.center.y;
+    this.center_x = obj.center.rel.x;
+    this.center_y = obj.center.rel.y;
+    this.orig_mtx = [obj[0], obj[1], obj[2], obj[3], obj[4], obj[5]];
 }
 
 
 exports.scale = scale;
-scale.prototype.start = scale_draw;
 scale.prototype.draw = scale_draw;
 
 function holder(app, coord) {
@@ -212,20 +174,11 @@
 
 
 
-function alpha_draw() {
+function alpha_draw(percent) {
 
     if (this.end == 1) return;
-    var percent = (Date.now() - this.starttime)/this.duration;
-    if (percent > 1) percent = 1;
     var sx = (this.targetalpha-this.startalpha)*percent+this.startalpha;
     this.obj.opacity=sx;
-
-    this.app.refresh();
-    var self = this;
-    if (percent < 1) {
-	this.obj.timer=setTimeout(function() { self.draw();}, frame_interval);
-	return;
-    }
     this.app.refresh();
     this.obj.animated_alpha = null;
 }
@@ -248,6 +201,120 @@
     this.duration = duration*1000;
 }
 
-alpha.prototype.start = alpha_draw;
 alpha.prototype.draw = alpha_draw;
 exports.alpha = alpha;
+
+function linear_update()
+{
+    var now = Date.now();
+    var i;
+    
+    sys.puts("real time is "+now);
+    sys.puts("end is "+this.end);
+    if (now >= this.end) {
+        this.timer.stop();
+	now = this.end;
+    }
+    if (now < this.startmove) return;
+    sys.puts("now is "+now+" offset is "+(now-this.startmove));
+    var per = (now-this.startmove)/this.duration/1000;
+    if (per > 1) per = 1;
+    this.action.draw(per);
+}
+
+function linear_start()
+{
+    var self = this;
+    if (this.timer)
+        this.timer.stop();
+    this.timer = setInterval(function() { self.update();}, frame_interval);
+    this.startmove = Date.now()+this.starttime*1000;
+    this.end = this.startmove+this.duration*1000;
+}
+function linear_stop() 
+{
+    if (this.timer) {
+        this.timer.stop();
+	this.timer = null;
+    }
+}
+
+function linear_finish()
+{
+    this.action.draw(1);
+}
+function linear(action,start, duration) 
+{
+    this.action = action;
+    this.duration = duration;
+    this.starttime = start;
+    this.timer=null;
+}
+linear.prototype.update = linear_update;
+linear.prototype.start = linear_start;
+linear.prototype.stop = linear_stop;
+linear.prototype.finish = linear_finish;
+exports.linear = linear;
+
+
+/* Following functions is not defined completed.
+   Mark it out to prevent error message.
+
+function multilinear(action,start,stages) {
+    sys.puts("Multilinear word is not implemented yet");
+}
+
+exports.multilinear = multilinear;
+multilinear.prototype.update = multilinear_update;
+multilinear.prototype.start = multilinear_start;
+multilinear.prototype.stop = multilinear_stop;
+multilinear.prototype.finish = multilinear_finish;
+
+function exponential(action,start,stages) {
+    sys.puts("exponential word is not implemented yet");
+}
+
+exports.exponential = exponential;
+exponential.prototype.update = exponential_update;
+exponential.prototype.start = exponential_start;
+exponential.prototype.stop = exponential_stop;
+exponential.prototype.finish = exponential_finish;
+
+*/
+
+function program(words)
+{
+    this.words = wrods;
+}
+
+program.prototype.start=function() {
+    for(w in this.words) {
+        w.start();
+    }
+}
+
+program.prototype.step=function(s) {
+    sys.puts("This function is not implemented yet");
+}
+program.prototype.stop=function() {
+    for(w in this.words) {
+        w.stop();
+    }
+}
+program.prototype.finish=function() {
+    for(w in this.words) {
+        w.finish();
+    }
+}
+
+exports.run = function(actions,start,duration) {
+    for(a in actions) {
+        var li = new linear(actions[a],start,duration);
+	sys.puts(li);
+	li.start();
+    }
+}
+exports.runexp=function(actions,start,exp) {
+    sys.puts("This function is not implemented yet");
+}
+
--- a/nodejs/desktop.svg	Sun Oct 03 18:12:11 2010 +0800
+++ b/nodejs/desktop.svg	Sun Oct 03 18:15:00 2010 +0800
@@ -154,15 +154,15 @@
      inkscape:pageshadow="2"
      inkscape:zoom="1.56875"
      inkscape:cx="207.9"
-     inkscape:cy="240"
+     inkscape:cy="290.99601"
      inkscape:document-units="px"
      inkscape:current-layer="layer1"
      showgrid="false"
-     inkscape:window-width="1920"
-     inkscape:window-height="1006"
-     inkscape:window-x="0"
-     inkscape:window-y="25"
-     inkscape:window-maximized="1" />
+     inkscape:window-width="1440"
+     inkscape:window-height="900"
+     inkscape:window-x="-1"
+     inkscape:window-y="-1"
+     inkscape:window-maximized="0" />
   <metadata
      id="metadata7">
     <rdf:RDF>
@@ -217,13 +217,13 @@
        transform="translate(-27.274116,-798.02051)"
        mbname="video"
        inkscape:transform-center-x="0"
-       inkscape:transform-center-y="0"
+       inkscape:transform-center-y="42.294399"
        inkscape:bbox-x="35.992421"
        inkscape:bbox-y="383.87928"
        inkscape:bbox-width="85.598946"
        inkscape:bbox-height="84.588799">
       <image
-         sodipodi:absref="/home/wycc/devel/md3/MadButterfly/nodejs/video.png"
+         sodipodi:absref="/usr/home/thinker/progm/MadButterfly/nodejs/video.png"
          xlink:href="video.png"
          width="85.598946"
          height="84.588799"
@@ -242,13 +242,13 @@
        transform="translate(126.77414,-798.02053)"
        mbname="audio"
        inkscape:transform-center-x="0"
-       inkscape:transform-center-y="0"
+       inkscape:transform-center-y="38.464287"
        inkscape:bbox-x="189.7836"
        inkscape:bbox-y="386.37278"
        inkscape:bbox-width="86.5"
        inkscape:bbox-height="76.928574">
       <image
-         sodipodi:absref="/home/wycc/devel/md3/MadButterfly/nodejs/music.png"
+         sodipodi:absref="/usr/home/thinker/progm/MadButterfly/nodejs/music.png"
          xlink:href="music.png"
          width="86.5"
          height="76.928574"
@@ -267,13 +267,13 @@
        transform="translate(290.67139,-799.03067)"
        mbname="picture"
        inkscape:transform-center-x="0"
-       inkscape:transform-center-y="0"
+       inkscape:transform-center-y="38.07143"
        inkscape:bbox-x="354.2955"
        inkscape:bbox-y="392.95458"
        inkscape:bbox-width="84"
        inkscape:bbox-height="76.14286">
       <image
-         sodipodi:absref="/home/wycc/devel/md3/MadButterfly/nodejs/photo.png"
+         sodipodi:absref="/usr/home/thinker/progm/MadButterfly/nodejs/photo.png"
          xlink:href="photo.png"
          width="84"
          height="76.14286"
@@ -292,13 +292,13 @@
        transform="translate(462.9024,-794.99007)"
        mbname="setting"
        inkscape:transform-center-x="0"
-       inkscape:transform-center-y="0"
+       inkscape:transform-center-y="37.57143"
        inkscape:bbox-x="526.42387"
        inkscape:bbox-y="389.0568"
        inkscape:bbox-width="84.571426"
        inkscape:bbox-height="75.14286">
       <image
-         sodipodi:absref="/home/wycc/devel/md3/MadButterfly/nodejs/tool.png"
+         sodipodi:absref="/usr/home/thinker/progm/MadButterfly/nodejs/tool.png"
          xlink:href="tool.png"
          width="84.571426"
          height="75.14286"
@@ -336,8 +336,8 @@
        id="text2931"
        inkscape:transform-center-x="0"
        inkscape:transform-center-y="0"
-       inkscape:bbox-x="140.30434"
-       inkscape:bbox-y="273.84843"
+       inkscape:bbox-x="126.28044"
+       inkscape:bbox-y="271.84843"
        inkscape:bbox-width="488.15625"
        inkscape:bbox-height="32.382812"
        mbname="line1"><tspan
@@ -348,8 +348,8 @@
          y="185.925"
          id="tspan2935"
          style="font-size:16px"
-         inkscape:bbox-x="140.56215"
-         inkscape:bbox-y="290.74687"
+         inkscape:bbox-x="126.53825"
+         inkscape:bbox-y="288.74687"
          inkscape:bbox-width="487.89844"
          inkscape:bbox-height="15.484375">Lim JeongHee (J.Lim) &amp; JoKwon (2AM) - The Road to Break Up</tspan><tspan
          sodipodi:role="line"
@@ -358,8 +358,8 @@
          x="124.96794"
          y="205.925"
          style="font-size:16px"
-         inkscape:bbox-x="140.30434"
-         inkscape:bbox-y="273.84843"
+         inkscape:bbox-x="126.28044"
+         inkscape:bbox-y="271.84843"
          inkscape:bbox-width="75.335938"
          inkscape:bbox-height="12.101562"
          id="tspan2941">7:00-7:30</tspan></text>
@@ -372,7 +372,7 @@
        inkscape:transform-center-x="0"
        inkscape:transform-center-y="0"
        inkscape:bbox-x="126.28044"
-       inkscape:bbox-y="212.01578"
+       inkscape:bbox-y="210.01578"
        inkscape:bbox-width="488.15625"
        inkscape:bbox-height="32.382812"
        mbname="line2"><tspan
@@ -384,7 +384,7 @@
          id="tspan2935-1"
          style="font-size:16px"
          inkscape:bbox-x="126.53825"
-         inkscape:bbox-y="228.91422"
+         inkscape:bbox-y="226.91422"
          inkscape:bbox-width="487.89844"
          inkscape:bbox-height="15.484375">Lim JeongHee (J.Lim) &amp; JoKwon (2AM) - The Road to Break Up</tspan><tspan
          sodipodi:role="line"
@@ -394,7 +394,7 @@
          y="267.75766"
          style="font-size:16px"
          inkscape:bbox-x="126.28044"
-         inkscape:bbox-y="212.01578"
+         inkscape:bbox-y="210.01578"
          inkscape:bbox-width="75.335938"
          inkscape:bbox-height="12.101562"
          id="tspan2941-9">7:30-8:30</tspan></text>
@@ -407,7 +407,7 @@
        inkscape:transform-center-x="0"
        inkscape:transform-center-y="0"
        inkscape:bbox-x="126.05388"
-       inkscape:bbox-y="150.18311"
+       inkscape:bbox-y="152.18311"
        inkscape:bbox-width="488.38281"
        inkscape:bbox-height="32.382812"
        mbname="line3"><tspan
@@ -419,7 +419,7 @@
          id="tspan2935-2"
          style="font-size:16px"
          inkscape:bbox-x="126.53825"
-         inkscape:bbox-y="167.08154"
+         inkscape:bbox-y="169.08154"
          inkscape:bbox-width="487.89844"
          inkscape:bbox-height="15.484375">Lim JeongHee (J.Lim) &amp; JoKwon (2AM) - The Road to Break Up</tspan><tspan
          sodipodi:role="line"
@@ -429,8 +429,8 @@
          y="329.59033"
          style="font-size:16px"
          inkscape:bbox-x="126.05388"
-         inkscape:bbox-y="150.18311"
-         inkscape:bbox-width="85.75"
+         inkscape:bbox-y="152.18311"
+         inkscape:bbox-width="75.5625"
          inkscape:bbox-height="12.101562"
          id="tspan2941-0">8:30-9:30</tspan></text>
     <text
@@ -442,7 +442,7 @@
        inkscape:transform-center-x="0"
        inkscape:transform-center-y="0"
        inkscape:bbox-x="125.97575"
-       inkscape:bbox-y="88.350433"
+       inkscape:bbox-y="90.350433"
        inkscape:bbox-width="488.46094"
        inkscape:bbox-height="32.382812"
        mbname="line4"><tspan
@@ -454,7 +454,7 @@
          id="tspan2935-11"
          style="font-size:16px"
          inkscape:bbox-x="126.53825"
-         inkscape:bbox-y="105.24887"
+         inkscape:bbox-y="107.24887"
          inkscape:bbox-width="487.89844"
          inkscape:bbox-height="15.484375">Lim JeongHee (J.Lim) &amp; JoKwon (2AM) - The Road to Break Up</tspan><tspan
          sodipodi:role="line"
@@ -464,7 +464,7 @@
          y="391.423"
          style="font-size:16px"
          inkscape:bbox-x="125.97575"
-         inkscape:bbox-y="88.350433"
+         inkscape:bbox-y="90.350433"
          inkscape:bbox-width="85.828125"
          inkscape:bbox-height="12.101562"
          id="tspan2941-90">9:30-10:30</tspan></text>
@@ -477,7 +477,7 @@
        inkscape:transform-center-x="0"
        inkscape:transform-center-y="0"
        inkscape:bbox-x="126.53825"
-       inkscape:bbox-y="26.517761"
+       inkscape:bbox-y="28.517761"
        inkscape:bbox-width="487.89844"
        inkscape:bbox-height="32.382812"
        mbname="line5"><tspan
@@ -489,7 +489,7 @@
          id="tspan2935-7"
          style="font-size:16px"
          inkscape:bbox-x="126.53825"
-         inkscape:bbox-y="43.416199"
+         inkscape:bbox-y="45.416199"
          inkscape:bbox-width="487.89844"
          inkscape:bbox-height="15.484375">Lim JeongHee (J.Lim) &amp; JoKwon (2AM) - The Road to Break Up</tspan><tspan
          sodipodi:role="line"
@@ -499,7 +499,7 @@
          y="453.25568"
          style="font-size:16px"
          inkscape:bbox-x="126.72575"
-         inkscape:bbox-y="26.517761"
+         inkscape:bbox-y="28.517761"
          inkscape:bbox-width="95.265625"
          inkscape:bbox-height="12.101562"
          id="tspan2941-7">10:30-11:30</tspan></text>
--- a/nodejs/mbapp.js	Sun Oct 03 18:12:11 2010 +0800
+++ b/nodejs/mbapp.js	Sun Oct 03 18:15:00 2010 +0800
@@ -5,6 +5,79 @@
 var sys = require("sys");
 var ldr = mbfly.img_ldr_new(".");
 
+function _reverse(m1) {
+    var rev = new Array(1, 0, 0, 0, 1, 0);
+    var m = new Array(m1[0], m1[1], m1[2], m1[3], m1[4], m1[5]);
+
+    rev[3] = -m[3] / m[0];
+    m[3] = 0;
+    m[4] += rev[3] * m[1];
+    m[5] += rev[3] * m[2];
+    
+    rev[1] = -m[1] / m[4];
+    rev[0] += rev[1] * rev[3];
+    m[1] = 0;
+    m[2] += rev[1] * m[5];
+    
+    rev[2] = -m[2];
+    rev[5] = -m[5];
+    
+    rev[0] = rev[0] / m[0];
+    rev[1] = rev[1] / m[0];
+    rev[2] = rev[2] / m[0];
+    
+    rev[3] = rev[3] / m[4];
+    rev[4] = rev[4] / m[4];
+    rev[5] = rev[5] / m[4];
+
+    return rev;
+}
+
+function _decorate_mb_rt(mb_rt) {
+    var coord;
+    
+    mb_rt._mbapp_saved_coord_new = mb_rt.coord_new;
+    mb_rt.coord_new = function(parent) {
+	var coord;
+	
+	coord = this._mbapp_saved_coord_new(parent);
+	coord.type = "coord";
+	coord.children = [];
+	coord._mbapp_saved_mtx = [coord[0], coord[1], coord[2],
+				  coord[3], coord[4], coord[5]];
+	coord._mbapp_saved_rev_mtx = _reverse(coord._mbapp_saved_mtx);
+	coord.parent = parent;
+	coord._mbapp_saved_add_shape = coord.add_shape;
+	coord.add_shape = function(shape) {
+	    var coord;
+	    
+	    this._mbapp_saved_add_shape(shape);
+	    shape.parent = this;
+	    this.children.push(shape);
+	}
+
+	parent.children.push(coord);
+	
+	return coord;
+    };
+
+    /*
+     * Decorate root coord
+     */
+    coord = mb_rt.root;
+    coord.type = "coord";
+    coord.children = [];
+    coord._mbapp_saved_mtx = [coord[0], coord[1], coord[2],
+			      coord[3], coord[4], coord[5]];
+	coord._mbapp_saved_rev_mtx = _reverse(coord._mbapp_saved_mtx);
+    coord._mbapp_saved_add_shape = coord.add_shape;
+    coord.add_shape = function(shape) {
+	var coord;
+	
+	this._mbapp_saved_add_shape(shape);
+	shape.parent = this;
+    }
+}
 
 app=function(display, w, h) {
     var self = this;
@@ -18,6 +91,7 @@
 	h = 480;
     
     mb_rt = this.mb_rt = new mbfly.mb_rt(display, w, h);
+    _decorate_mb_rt(mb_rt);
     var background = mb_rt.rect_new(0, 0, 720, 480, 0, 0);
     var paint = mb_rt.paint_color_new(1, 1, 1, 1);
     paint.fill(background);
@@ -32,7 +106,6 @@
 }
 
 app.prototype.KeyPress = function(evt) {
-    sys.puts(evt.sym);
     if (this.onKeyPress) this.onKeyPress(evt.sym);
 	if (evt.sym in this.keymap) this.keymap[evt.sym]();
 }
@@ -52,7 +125,7 @@
     return this.mb_rt.kbevents.add_event_observer(type,f);    
 }
 app.prototype.refresh=function() {
-    this.mb_rt.redraw_all();
+    this.mb_rt.redraw_changed();
     this.mb_rt.flush();
 }
 app.prototype.dump=function() {
@@ -79,6 +152,7 @@
 	throw "Invalid argument";
     
     mb_rt = this.mb_rt = new mbfly.mb_rt_with_win(display, win);
+    _decorate_mb_rt(mb_rt);
     background = mb_rt.rect_new(0, 0, 720, 480, 0, 0);
     paint = mb_rt.paint_color_new(1, 1, 1, 1);
     paint.fill(background);
--- a/nodejs/phone.js	Sun Oct 03 18:12:11 2010 +0800
+++ b/nodejs/phone.js	Sun Oct 03 18:15:00 2010 +0800
@@ -41,19 +41,19 @@
 	});
     icon.mouse_event.add_event_observer(4, function(evt) {
 	    pressholder.go_center(evt.cur_tgt);
-	    var rotate = new animate.rotate(app, evt.cur_tgt, 2 * 3.1415, 0.7);
-	    rotate.start();
+	    var rotate = new animate.rotate(app, evt.cur_tgt, 2 * 3.1415);
+	    animate.run([rotate], 0, 0.7);
 	});
 }
 
 var sw = 0;
-var dock_up = new animate.linear(app, dock, 0, -300, 0.5);
-var dock_down = new animate.linear(app, dock, 0, 0, 0.2);
+var dock_up = new animate.shift(app, dock, 0, -300);
+var dock_down = new animate.shift(app, dock, 0, 0);
 dock.mouse_event.add_event_observer(4, function(evt) {
 	if(sw == 0) {
-	    dock_up.start();
+	    animate.run([dock_up], 0, 0.5);
 	} else {
-	    dock_down.start();
+	    animate.run([dock_down], 0, 0.2);
 	}
 	sw = sw ^ 1;
     });
--- a/nodejs/shapes.cc	Sun Oct 03 18:12:11 2010 +0800
+++ b/nodejs/shapes.cc	Sun Oct 03 18:15:00 2010 +0800
@@ -70,6 +70,25 @@
     }
 }
 
+static void
+xnjsmb_sh_stext_set_text(shape_t *sh, Handle<Object> self,
+			 const char *txt) {
+    Handle<Object> rt;
+    redraw_man_t *rdman;
+    
+    sh_stext_set_text(sh, txt);
+    
+    /*
+     * Mark changed.
+     */
+    rt = GET(self, "mbrt")->ToObject();
+    ASSERT(rt != NULL);
+    rdman = xnjsmb_rt_rdman(rt);
+
+    if(sh_get_coord(sh))
+	rdman_shape_changed(rdman, sh);
+}
+
 /*! \brief Set style blocks for a stext object from JS.
  *
  * A style block is style setting of a chip of text.  It is a 3-tuple,
--- a/nodejs/shapes.m4	Sun Oct 03 18:12:11 2010 +0800
+++ b/nodejs/shapes.m4	Sun Oct 03 18:15:00 2010 +0800
@@ -12,7 +12,8 @@
        (([INHERIT], [shape]), ([STMOD], [xnjsmb_shape_mod])))
 
 STRUCT([stext], [shape_t], [],
-       [METHOD([set_text], [sh_stext_set_text], (STR([txt])), 1, []),
+       [METHOD([set_text], [xnjsmb_sh_stext_set_text],
+       			    (SELF, STR([txt])), 1, []),
         METHOD([set_style], [xnjsmb_sh_stext_set_style],
 	       (SELF, ARRAY([blks]), ERR), 1, [])],
        (([INHERIT], [shape]), ([STMOD], [xnjsmb_shape_mod])))
--- a/nodejs/svg.js	Sun Oct 03 18:12:11 2010 +0800
+++ b/nodejs/svg.js	Sun Oct 03 18:15:00 2010 +0800
@@ -12,6 +12,26 @@
     "red": [1, 0, 0]
 };
 
+function parse_color(color) {
+    var r, g, b;
+    var c;
+    
+    if (color[0] == "#") {
+	r = parseInt(color.substring(1, 3), 16) / 255;
+	g = parseInt(color.substring(3, 5), 16) / 255;
+	b = parseInt(color.substring(5, 7), 16) / 255;
+    } else if(_std_colors[color]) {
+	c = _std_colors[color];
+	r = c[0];
+	g = c[1];
+	b = c[2];
+    } else {
+	r = g = b = 0;
+    }
+    
+    return [r, g, b];
+}
+
 exports.loadSVG=function(mb_rt,root,filename) {
     return new loadSVG(mb_rt, root, filename);
 };
@@ -27,6 +47,7 @@
     this.mb_rt = mb_rt;
     this.stop_ref={};
     this.gradients={};
+    this.radials = {};
     root.center=new Object();
     root.center.x = 10000;
     root.center.y = 10000;
@@ -85,6 +106,33 @@
 	
 }
 
+function parse_style(node) {
+    var style_attr;
+    var style;
+    var parts, part;
+    var kv, key, value;
+    var content = {};
+    var i;
+    
+    style_attr = node.attr('style');
+    if(!style_attr)
+	return content;
+
+    style = style_attr.value();
+    parts = style.split(';');
+    for(i = 0; i < parts.length; i++) {
+	part = parts[i].trim();
+	if(part) {
+	    kv = part.split(':');
+	    key = kv[0].trim();
+	    value = kv[1].trim();
+	    content[key] = value;
+	}
+    }
+
+    return content;
+}
+
 function parseColor(c)
 {
     if (c[0] == '#') {
@@ -138,29 +186,62 @@
     this.text.set_text(text); 
 }
 
-loadSVG.prototype.parseTSpan = function(coord, n,style) {
+function _parse_font_size(fn_sz_str) {
+    var pos;
+
+    pos = fn_sz_str.search("px");
+    if(pos >= 0)
+	fn_sz_str = fn_sz_str.substring(0, pos);
+    pos = fn_sz_str.search("pt");
+    if(pos >= 0)
+	fn_sz_str = fn_sz_str.substring(0, pos);
+
+    return parseFloat(fn_sz_str);
+}
+
+loadSVG.prototype.parseTSpan = function(coord, n, pstyle) {
     var x = getInteger(n,'x');
     var y = getInteger(n,'y');
     var tcoord = this.mb_rt.coord_new(coord);
-    var nodes = n.childNodes();
+    var style;
+    var family = "Courier";
+    var weight = 80;
+    var slant = 0;
+    var sz = 10;
+    var face;
     var k;
-    
     var obj = this.mb_rt.stext_new(n.text(),x,y);
-    parseTextStyle(style,n);
-    style.paint = this.mb_rt.paint_color_new(1,1,1,1);
-    style.face=this.mb_rt.font_face_query(style.family, 2, 100);
-    obj.set_style([[20,style.face,style.fs]]);
-    style.paint.fill(obj);
+    
+    style = parse_style(n);
+    for(k in pstyle) {
+	if(k in style)
+	    continue;
+	style[k] = pstyle[k];
+    }
+
+    if("font-family" in style)
+	family = style["font-family"];
+    if("font-size" in style)
+	sz = _parse_font_size(style["font-size"]);
+    if("font-weight" in style) {
+	if(style["font-weight"] == "bold")
+	    weight = 200;
+    }
+    if("font-style" in style) {
+	if(style["font-style"] == "oblique")
+	    slant = 110;
+    }
+
+    face = this.mb_rt.font_face_query(family, slant, weight);
+    obj.set_style([[n.text().length, face, sz]]);
+
     tcoord.add_shape(obj);
-    for(k in nodes) {
-        var name = nodes[k].name();
-        if (name == "tspan") {
-            this.parseTSpan(tcoord,nodes[k]);
-        } else {
-        }
-    }
-    tcoord.set_text=tspan_set_text;
+    tcoord.set_text = tspan_set_text;
     tcoord.text = obj;
+    
+    this._set_paint_style(style, obj);
+    this._set_bbox(n, obj);
+    
     make_mbnames(this.mb_rt, n, tcoord);
 };
 
@@ -179,8 +260,15 @@
 	paint = this.mb_rt.paint_color_new(c[0], c[1], c[2], alpha);
     } else if (color.substring(0,3) == 'url') {
 	var id = color.substring(5, color.length-1);
-	var gr = this.gradients[id];
-	paint = this.mb_rt.paint_linear_new(gr[0],gr[1],gr[2],gr[3]);
+	if(id in this.gradients) {
+	    var gr = this.gradients[id];
+	    paint = this.mb_rt.paint_linear_new(gr[0],gr[1],gr[2],gr[3]);
+	} else {
+	    var radial = this.radials[id];
+	    paint = this.mb_rt.paint_radial_new(radial[0],
+						radial[1],
+						radial[2]);
+	}
 	paint.set_stops(this.stop_ref[id]);
     } else {
 	paint = this.mb_rt.paint_color_new(0,0,0,1);
@@ -246,121 +334,437 @@
 	coord.center.y = miny;
 };
 
+function _mul(m1, m2) {
+    var res = new Array();
+
+    res.push(m1[0] * m2[0] + m1[1] * m2[3]);
+    res.push(m1[0] * m2[1] + m1[1] * m2[4]);
+    res.push(m1[0] * m2[2] + m1[1] * m2[5] + m1[2]);
+    res.push(m1[3] * m2[0] + m1[4] * m2[3]);
+    res.push(m1[3] * m2[1] + m1[4] * m2[4]);
+    res.push(m1[3] * m2[2] + m1[4] * m2[5] + m1[5]);
+
+    return res;
+}
+
+function _pnt_transform(x, y, matrix) {
+    var rx, ry;
+
+    rx = x * matrix[0] + y * matrix[1] + matrix[2];
+    ry = x * matrix[3] + y * matrix[4] + matrix[5];
+    return new Array(rx, ry);
+}
+
+function _shift_transform(x, y, matrix) {
+    var rx, ry;
+
+    rx = x * matrix[0] + y * matrix[1];
+    ry = x * matrix[3] + y * matrix[4];
+    return new Array(rx, ry);
+}
+
+function _transform_bbox(bbox, matrix) {
+    var min_x, min_y, max_x, max_y;
+    var x, y;
+    var pnt;
+    var pnts = new Array();
+    var i;
+
+    pnt = _pnt_transform(bbox.x, bbox.y, matrix);
+    pnts.push(pnt);
+    pnt = _pnt_transform(bbox.x + bbox.width, bbox.y, matrix);
+    pnts.push(pnt);
+    pnt = _pnt_transform(bbox.x, bbox.y + bbox.height, matrix);
+    pnts.push(pnt);
+    pnt = _pnt_transform(bbox.x + bbox.width, bbox.y + bbox.height, matrix);
+    pnts.push(pnt);
+
+    min_x = max_x = pnts[0][0];
+    min_y = max_y = pnts[0][1];
+    for(i = 1; i < pnts.length; i++) {
+	pnt = pnts[i];
+	if(pnt[0] < min_x)
+	    min_x = pnt[0];
+	if(pnt[1] < min_y)
+	    min_y = pnt[1];
+	if(pnt[0] > max_x)
+	    max_x = pnt[0];
+	if(pnt[1] > max_y)
+	    max_y = pnt[1];
+    }
+
+    bbox.x = min_x;
+    bbox.y = min_y;
+    bbox.width = max_x - min_x;
+    bbox.height = max_y - min_y;
+}
+
+function _reverse(m1) {
+    var rev = new Array(1, 0, 0, 0, 1, 0);
+    var m = new Array(m1[0], m1[1], m1[2], m1[3], m1[4], m1[5]);
+
+    rev[3] = -m[3] / m[0];
+    m[3] = 0;
+    m[4] += rev[3] * m[1];
+    m[5] += rev[3] * m[2];
+    
+    rev[1] = -m[1] / m[4];
+    rev[0] += rev[1] * rev[3];
+    m[1] = 0;
+    m[2] += rev[1] * m[5];
+    
+    rev[2] = -m[2];
+    rev[5] = -m[5];
+    
+    rev[0] = rev[0] / m[0];
+    rev[1] = rev[1] / m[0];
+    rev[2] = rev[2] / m[0];
+    
+    rev[3] = rev[3] / m[4];
+    rev[4] = rev[4] / m[4];
+    rev[5] = rev[5] / m[4];
+
+    return rev;
+}
+
+var _bbox_proto = {
+    _get_ac_saved_rev: function() {
+	var c = this.owner;
+	var mtx;
+	
+	if(c.type != "coord")
+	    c = c.parent;	// is a shape!
+	
+	mtx = c._mbapp_saved_rev_mtx;
+	while(c.parent && typeof c.parent != "undefined") {
+	    c = c.parent;
+	    mtx = _mul(mtx, c._mbapp_saved_rev_mtx);
+	}
+
+	return mtx;
+    },
+    
+    _get_ac_mtx: function() {
+	var c = this.owner;
+	var mtx;
+	
+	if(c.type != "coord")
+	    c = c.parent;	// is a shape!
+
+	mtx = [c[0], c[1], c[2], c[3], c[4], c[5]];
+	while(c.parent) {
+	    c = c.parent;
+	    mtx = _mul(c, mtx);
+	}
+
+	return mtx;
+    },
+
+    _saved_to_current: function() {
+	var r;
+	
+	r = _mul(this._get_ac_mtx(), this._get_ac_saved_rev());
+	
+	return r;
+    },
+
+    /*! \brief Update x, y, width, and height of the bbox.
+     */
+    update: function() {
+	var mtx;
+
+	this.x = this.orig.x;
+	this.y = this.orig.y;
+	this.width = this.orig.width;
+	this.height = this.orig.height;
+	
+	mtx = this._saved_to_current();
+	_transform_bbox(this, mtx);
+    },    
+};
+
+var _center_proto = {
+    _get_ac_saved_rev: function() {
+	var c = this.owner;
+	var mtx;
+	
+	if(c.type != "coord")
+	    c = c.parent;	// is a shape!
+	
+	mtx = c._mbapp_saved_rev_mtx;
+	while(c.parent && typeof c.parent != "undefined") {
+	    c = c.parent;
+	    mtx = _mul(mtx, c._mbapp_saved_rev_mtx);
+	}
+
+	return mtx;
+    },
+    
+    _get_ac_mtx: function() {
+	var c = this.owner;
+	var mtx;
+	
+	if(c.type != "coord")
+	    c = c.parent;	// is a shape!
+
+	mtx = [c[0], c[1], c[2], c[3], c[4], c[5]];
+	while(c.parent) {
+	    c = c.parent;
+	    mtx = _mul(c, mtx);
+	}
+
+	return mtx;
+    },
+
+    _get_ac_rev: function() {
+	var c = this.owner;
+	var mtx;
+	
+	if(c.type != "coord")
+	    c = c.parent;	// is a shape!
+
+	mtx = _reverse([c[0], c[1], c[2], c[3], c[4], c[5]]);
+	while(c.parent) {
+	    c = c.parent;
+	    mtx = _mul(mtx, _reverse([c[0], c[1], c[2], c[3], c[4], c[5]]));
+	}
+
+	return mtx;
+    },
+
+    _saved_to_current: function() {
+	var r;
+	
+	r = _mul(this._get_ac_mtx(), this._get_ac_saved_rev());
+	
+	return r;
+    },
+
+    /*! \brief Update x, y of center point of an object.
+     */
+    update: function() {
+	var mtx;
+	var xy;
+
+	mtx = this._saved_to_current();
+	xy = _pnt_transform(this.orig.x, this.orig.y, mtx);
+
+	this._x = xy[0];
+	this._y = xy[1];
+    },
+
+    /*! \brief Move owner object to make center at (x, y).
+     */
+    move: function(x, y) {
+	var mtx;
+	var xdiff = x - this._x;
+	var ydiff = y - this._y;
+	var shiftxy;
+	var c;
+
+	mtx = this._get_ac_rev();
+	shiftxy = _shift_transform(xdiff, ydiff, mtx);
+
+	c = this.owner;
+	if(c.type != "coord")
+	    c = c.parent;
+
+	c[2] += shiftxy[0];
+	c[5] += shiftxy[1];
+
+	this._x = x;
+	this._y = y;
+    },
+
+    /*! \brief Move owner object to make center at position specified by pnt.
+     */
+    move_pnt: function(pnt) {
+	this.move(pnt.x, pnt.y);
+    },
+
+    /*! \brief Prevent user to modify value.
+     */
+    get x() { return this._x; },
+    
+    /*! \brief Prevent user to modify value.
+     */
+    get y() { return this._y; },
+
+    get rel() {
+	var rev;
+	var xy;
+	
+	rev = this._get_ac_rev();
+	xy = _pnt_transform(this.orig.x, this.orig.y, rev);
+
+	return {x: xy[0], y: xy[1]};
+    },
+};
 
 loadSVG.prototype._set_bbox = function(node, tgt) {
     var a;
     var vstr;
     var bbox, center;
+    var orig;
     
     a = node.attr("bbox-x");
-    sys.puts("a="+a);
     if(!a)
 	return 0;
     
+    /* bbox.orig is initial values of bbox.  The bbox is recomputed
+     * according bbox.orig and current matrix.  See bbox.update().
+     */
     tgt.bbox = bbox = new Object();
+    bbox.orig = orig = new Object();
+    bbox.owner = tgt;
+    bbox.__proto__ = _bbox_proto;
     vstr = a.value();
-    bbox.x = parseFloat(vstr);
+    orig.x = parseFloat(vstr);
 
     a = node.attr("bbox-y");
     vstr = a.value();
-    bbox.y = this.height - parseFloat(vstr);
+    orig.y = this.height - parseFloat(vstr);
 
     a = node.attr("bbox-width");
     vstr = a.value();
-    bbox.width = parseFloat(vstr);
+    orig.width = parseFloat(vstr);
 
     a = node.attr("bbox-height");
     vstr = a.value();
-    bbox.height = parseFloat(vstr);
-    bbox.y -= bbox.height;
+    orig.height = parseFloat(vstr);
+    orig.y -= orig.height;
+    
+    bbox.update();
 
+    /* center.orig is initial values of center.  The center is
+     * recomputed according center.orig and current matrix.  See
+     * center.update().
+     */
     tgt.center = center = new Object();
+    center.orig = orig = new Object();
     
-    center.x = bbox.width / 2 + bbox.x;
-    center.y = bbox.height / 2 + bbox.y;
-    //center.x = bbox.x;
-    //center.y = bbox.y;
+    orig.x = bbox.orig.width / 2 + bbox.orig.x;
+    orig.y = bbox.orig.height / 2 + bbox.orig.y;
     a = node.attr("transform-center-x");
-    if(!a)
-	return 1;
+    if(a) {
+	vstr = a.value();
+	orig.x += parseFloat(vstr);
+	a = node.attr("transform-center-y");
+	vstr = a.value();
+	orig.y -= parseFloat(vstr);
+    }
+    center.__proto__ = _center_proto;
+    center.owner = tgt;
+    center.update();
     
-    vstr = a.value();
-    center.x += parseFloat(vstr);
-    a = node.attr("transform-center-y");
-    vstr = a.value();
-    center.y -= parseFloat(vstr);
     return 1;
 }
 
-loadSVG.prototype._set_paint = function(node, tgt) {
-    var style = node.attr('style');
+loadSVG.prototype._set_paint_style = function(style, tgt) {
     var paint;
     var fill_alpha = 1;
     var stroke_alpha = 1;
+    var alpha = 1;
     var fill_color;
     var stroke_color;
-    var black_paint;
+    var stroke_width = 1;
     var i;
     
-    if(style != null) {
-	var items = style.value().split(';');
-	var alpha;
-	
-	for(i in items) {
-	    var f = items[i].split(':');
-	    if (f[0] == 'opacity') {
-		alpha = f[1];
-	    } else if (f[0] == 'fill') {
-		fill_color = f[1];
-	    } else if (f[0] == 'fill-opacity') {
-		fill_alpha = parseFloat(f[1]);
-	    } else if (f[0] == 'stroke') {
-		stroke_color = f[1];
-	    } else if (f[0] == 'stroke-width') {
-		tgt.stroke_width = parseFloat(f[1]);
-	    } else if (f[0] == 'stroke-opacity') {
-		stroke_alpha = parseFloat(f[1]);
-	    } else if (f[0] == 'display') {
-		if(f[1] == 'none')
-		    return;
-	    }
-	}
-
+    if(style) {
+	if('opacity' in style)
+	    alpha = parseFloat(style['opacity']);
+	if('fill' in style)
+	    fill_color = style['fill'];
+	if('fill-opacity' in style)
+	    fill_alpha = parseFloat(style['fill-opacity']);
+	if('stroke' in style)
+	    stroke_color = style['stroke'];
+	if('stroke-width' in style)
+	    stroke_width = parseFloat(style['stroke-width']);
+	if('stroke-opacity' in style)
+	    stroke_alpha = parseFloat(style['stroke-opacity']);
+	if('display' in style && style['display'] == 'none')
+	    return;
     }
 
-    if(!fill_color || !stroke_color)
-	black_paint = this.mb_rt.paint_color_new(0, 0, 0, 1);
-    
     if(fill_color) {
 	if(fill_color != "none") {
 	    paint = this._prepare_paint_color(fill_color, fill_alpha);
 	    paint.fill(tgt);
 	}
-    } else {
-	black_paint.fill(tgt);
     }
     if(stroke_color) {
 	if(stroke_color != "none") {
 	    paint = this._prepare_paint_color(stroke_color, stroke_alpha);
 	    paint.stroke(tgt);
 	}
-    } else {
-	black_paint.stroke(tgt);
     }
+
+    tgt.stroke_width = stroke_width;
+    
+    if(alpha < 1)
+	tgt.parent.opacity = alpha;
 };
 
+loadSVG.prototype._set_paint = function(node, tgt) {
+    var style;
+
+    style = parse_style(node);
+    this._set_paint_style(style, tgt);
+};
+
+function formalize_path_data(d) {
+    var posM, posm;
+    var pos;
+    var nums = "0123456789+-.";
+    var rel = false;
+    var cmd;
+
+    posM = d.search("M");
+    posm = d.search("m");
+    pos = posm < posM? posm: posM;
+    if(pos == -1)
+	pos = posM == -1? posm: posM;
+    if(pos == -1)
+	return d;
+
+    if(posm == pos)
+	rel = true;
+    
+    pos = pos + 1;
+    while(pos < d.length && " ,".search(d[pos]) >= 0)
+	pos++;
+    while(pos < d.length && nums.search(d[pos]) >= 0)
+	pos++;
+    while(pos < d.length && " ,".search(d[pos]) >= 0)
+	pos++;
+    while(pos < d.length && nums.search(d[pos]) >= 0)
+	pos++;
+    while(pos < d.length && " ,".search(d[pos]) >= 0)
+	pos++;
+    if(nums.search(d[pos]) >= 0) {
+	if(rel)
+	    cmd = "l";
+	else
+	    cmd = "L";
+	d = d.substring(0, pos) + cmd + formalize_path_data(d.substring(pos));
+    }
+    return d;
+}
+
 loadSVG.prototype.parsePath=function(accu, coord,id, n)
 {
-    var d = n.attr('d').value();
+    var d = formalize_path_data(n.attr('d').value());
     var style = n.attr('style');
     var path = this.mb_rt.path_new(d);
-    
-    guessPathBoundingBox(coord,d);
-    this._set_bbox(n, path);
+    var pcoord = this.mb_rt.coord_new(coord);
+
+    guessPathBoundingBox(pcoord,d);
+    pcoord.add_shape(path);
     this._set_paint(n, path);
-    coord.add_shape(path);
+    this._set_bbox(n, path);
 
-    make_mbnames(this.mb_rt, n, path);
+    make_mbnames(this.mb_rt, n, pcoord);
 };
 
 loadSVG.prototype.parseText=function(accu,coord,id, n)
@@ -368,7 +772,7 @@
     var x = getInteger(n,'x');
     var y = getInteger(n,'y');
     var tcoord = this.mb_rt.coord_new(coord);
-    var style = new Object();
+    var style;
 
     if (n.attr('x')) {
 	var nx = coord[0]*x+coord[1]*y+coord[2];
@@ -380,9 +784,7 @@
 	if (coord.center.y > ny)
 	    coord.center.y = ny;
     }
-    style.fs = 20;
-    style.family = 'courier';
-    parseTextStyle(style,n);
+    style = parse_style(n);
     var nodes = n.childNodes();
     var k;
     for(k in nodes) {
@@ -392,16 +794,7 @@
 	} else {
 	}
     }
-    sys.puts(y);
-    if (this._set_bbox(n, tcoord)) {
-        tcoord.center.x -= tcoord[2];
-        tcoord.center.y -= tcoord[5];
-        tcoord._x = tcoord.center.x;
-        tcoord._y = tcoord.center.y;
-    } else {
-        tcoord._x = coord.center.x;
-        tcoord._y = coord.center.y;
-    }
+    this._set_bbox(n, tcoord);
 	
     make_mbnames(this.mb_rt, n, tcoord);
 };
@@ -478,25 +871,9 @@
         parseTransform(tcoord,trans.value());
 
     var rect = this.mb_rt.rect_new(x,y,w,h,10, 10);
+    tcoord.add_shape(rect);
     this._set_paint(n, rect);
-    if (this._set_bbox(n, tcoord)) {
-	tcoord.center.x -= tcoord[2];
-	tcoord.center.y -= tcoord[5];
-    } else {
-        if (trans) {
-            rx = tcoord[0]*x+tcoord[1]*y+tcoord[2];
-            ry = tcoord[3]*x+tcoord[4]*y+tcoord[5];
-            if (tcoord.center.x > rx)
-                tcoord.center.x = rx;
-            if (tcoord.center.y > ry)
-                tcoord.center.y = ry;
-	}
-    }
-    sys.puts("center.x="+tcoord.center.x);
-    sys.puts("center.y="+tcoord.center.y);
-    tcoord._x = tcoord.center.x;
-    tcoord._y = tcoord.center.y;
-    tcoord.add_shape(rect);
+    this._set_bbox(n, tcoord);
 
     make_mbnames(this.mb_rt, n, tcoord);
 };
@@ -555,7 +932,7 @@
     style = {};
     parseGroupStyle(style, n);
     if(style.opacity) {
-	sys.puts(style.opacity);
+	sys.puts("opacity=" + style.opacity);
 	coord.opacity=style.opacity;
     }
 
@@ -583,13 +960,7 @@
     if (root.center.y > coord.center.y)
 	root.center.y = coord.center.y;
 
-    if (this._set_bbox(n, coord)) {
-        coord.center.x -= accu[2];
-        coord.center.y -= accu[5];
-    }
-    coord._x = coord.center.x;
-    coord._y = coord.center.y;
-    sys.puts("coord.center.x="+coord.center.x+",coord.center.y="+coord.center.y);
+    this._set_bbox(n, coord);
     
     make_mbnames(this.mb_rt, n, coord);
 };
@@ -641,12 +1012,54 @@
     paint.fill(img);
     tcoord.add_shape(img);
     
-    if (this._set_bbox(n, img)) {
-        img.center.x -= accu[2]+coord[2];
-        img.center.y -= accu[5]+coord[5];
+    this._set_bbox(n, img);
+    
+    make_mbnames(this.mb_rt, n, img);
+};
+
+function _parse_stops(n) {
+    var children;
+    var child;
+    var style;
+    var color;
+    var rgb;
+    var opacity;
+    var r, g, b, a;
+    var offset_atr, offset;
+    var stops = [];
+    var i;
+
+    children = n.childNodes();
+    for(i = 0; i < children.length; i++) {
+	child = children[i];
+	if(child.name() == "stop") {
+	    style = parse_style(child);
+	    
+	    color = style["stop-color"];
+	    if(color) {
+		rgb = parse_color(color);
+		r = rgb[0];
+		g = rgb[1];
+		b = rgb[2];
+	    }
+	    
+	    opacity = style["stop-opacity"];
+	    if(opacity)
+		a = parseFloat(opacity);
+	    else
+		a = 1;
+	    
+	    offset_attr = child.attr("offset");
+	    if(offset_attr)
+		offset = parseFloat(offset_attr.value());
+	    else
+		offset = 0;
+	    
+	    stops.push([offset, r, g, b, a]);
+	}
     }
 
-    make_mbnames(this.mb_rt, n, img);
+    return stops;
 };
 
 loadSVG.prototype._MB_parseLinearGradient=function(root,n)
@@ -654,71 +1067,111 @@
     var id = n.attr('id');
     var k;
     var nodes = n.childNodes();
+    var mtx = [1, 0, 0, 0, 1, 0];
 
     if (id == null) return;
+    id = id.value();
+
     var x1 = n.attr("x1");
     var y1 = n.attr("y1");
     var x2 = n.attr("x2");
     var y2 = n.attr("y2");
+    var xy;
     var gr;
     var color, opacity;
     var stops;
     var r,g,b;
-    stops=[];
-    for(k in nodes) {
-	var ss = nodes[k];
-	if (ss.name()=="stop") {
-	    var style = ss.attr("style").value();
-	    var items = style.split(';');
-	    var off = parseInt(ss.attr('offset').value());
-	    color = 'black';
-	    opacity = 1;
-	    for (i in items) {
-		it = items[i];
-		var f = it.split(':');
-		k = f[0];
-		v = f[1];
-		if (k == 'stop-color') {
-		    color = v.substring(1);
-		    if (v == 'white') {
-			r = 1;
-			g = 1;
-			b = 1;
-		    } else if (v == 'black') {
-			r = 0;
-			g = 0;
-			b = 0;
-		    } else {
-			r = parseInt(color.substring(0,2),16)/255.0;
-			g = parseInt(color.substring(2,4),16)/255.0;
-			b = parseInt(color.substring(4,6),16)/255.0;
-		    }
-		} else if (k=='stop-opacity') {
-		    opacity = parseFloat(v);
-		}
-	    }
-	    stops.push([off, r,g,b,opacity]);
-	}
+    
+    if(x1)
+	x1 = parseFloat(x1.value());
+    if(x2)
+	x2 = parseFloat(x2.value());
+    if(y1)
+	y1 = parseFloat(y1.value());
+    if(y2)
+	y2 = parseFloat(y2.value());
+    
+    stops = _parse_stops(n);
+    
+    var href = n.attr('href');
+    if(href != null) {
+	href = href.value();
+	var hrefid = href.substring(1);
+	pstops = this.stop_ref[hrefid];
+	stops = pstops.concat(stops);
+	
+	var hrefgr = this.gradients[hrefid];
+	if(typeof x1 == "undefined")
+	    x1 = hrefgr[0];
+	if(typeof y1 == "undefined")
+	    y1 = hrefgr[1];
+	if(typeof x2 == "undefined")
+	    x2 = hrefgr[2];
+	if(typeof y2 == "undefined")
+	    y2 = hrefgr[3];
     }
-    var href = n.attr('href');
-    if (href != null) {
-	href = href.value();
-	pstops = this.stop_ref[href.substring(1)];
-	stops = pstops.concat(stops);
+
+    if(n.attr("gradientTransform")) {
+	parseTransform(mtx, n.attr("gradientTransform").value());
+	xy = _pnt_transform(x1, y1, mtx);
+	x1 = xy[0];
+	y1 = xy[1];
+	xy = _pnt_transform(x2, y2, mtx);
+	x2 = xy[0];
+	y2 = xy[1];
     }
-    id = id.value();
+
     this.stop_ref[id] = stops;
-    if (x1)
-	x1 = parseFloat(x1.value());
-    if (x2)
-	x2 = parseFloat(x2.value());
-    if (y1)
-	y1 = parseFloat(y1.value());
-    if (y2)
-	y2 = parseFloat(y2.value());
     this.gradients[id] = [x1,y1,x2,y2];
 };
 
+loadSVG.prototype._MB_parseRadialGradient = function(root,n) {
+    var stops;
+    var cx, cy;
+    var xy;
+    var mtx = [1, 0, 0, 0, 1, 0];
+    var id;
+    var href;
+    var r;
+
+    id = n.attr("id");
+    if(!id)
+	throw "Require an id";
+    id = id.value();
+
+    stops = _parse_stops(n);
+    
+    cx = n.attr("cx");
+    if(!cx)
+	throw "Miss cx attribute";
+    cy = n.attr("cy");
+    if(!cy)
+	throw "Miss cy attribute";
+    cx = parseFloat(cx.value());
+    cy = parseFloat(cy.value());
+
+    r = n.attr("r");
+    if(!r)
+	throw "Miss r attribute";
+    r = parseFloat(r.value());
+
+    href = n.attr("href");
+    if(href) {
+	href = href.value().substring(1);
+	stops = this.stop_ref[href];
+    }
+
+    if(n.attr("gradientTransform")) {
+	parseTransform(mtx, n.attr("gradientTransform").value());
+	xy = _pnt_transform(cx, cy, mtx);
+	cx = xy[0];
+	cy = xy[1];
+    }
+
+    this.radials[id] = [cx, cy, r];
+    this.stop_ref[id] = stops;
+}
+
 loadSVG.prototype.parseDefs=function(root,n)
 {
     var k;
@@ -728,6 +1181,8 @@
 	var name = nodes[k].name();
 	if (name == "linearGradient") {
 	    this._MB_parseLinearGradient(root,nodes[k]);
+	} else if(name == "radialGradient") {
+	    this._MB_parseRadialGradient(root,nodes[k]);
 	}
     }
 };
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/svgviewer.js	Sun Oct 03 18:15:00 2010 +0800
@@ -0,0 +1,8 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
+var svg = require("./svg");
+var mbapp = require("./mbapp");
+
+app = new mbapp.app();
+app.loadSVG("test.svg");
+app.loop();
--- a/nodejs/testdesktop.js	Sun Oct 03 18:12:11 2010 +0800
+++ b/nodejs/testdesktop.js	Sun Oct 03 18:15:00 2010 +0800
@@ -1,3 +1,4 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
 // vim: sw=4:ts=8:sts=4
 var svg = require("./svg");
 var mbapp = require("./mbapp");
@@ -16,97 +17,110 @@
 setting = app.get("setting");
 
 lightbar = app.get("lightbar");
-lines=[app.get("line1"),app.get("line2"),app.get("line3"), app.get("line4"),app.get("line5")];
-for(i=0;i<lines.length;i++) {
-    sys.puts("["+i+"]="+lines[i].y);
+lines = [];
+for(i = 0; i < 5; i++) {
+    line = app.get("line" + (i + 1));
+    lines.push(line);
 }
 line=0;
 
-items=[video,audio,picture,setting];
+items=[video, audio, picture, setting];
+
 item = 0;
-an = new animate.scale(app,items[item],1,1.5,0.1);
-an.start();
+
+animate.run([new animate.scale(app,items[item], 1, 1.5)], 0, 0.1);
 app.refresh();
+
 app.addKeyListener(mbapp.KEY_LEFT, function() {
-	var old = items[item];
-	item = item - 1;
-	if (item == -1) {
-		item = 0;
-		return;
-	}
-        var target = items[item];
-	var an = new animate.scale(app,old,1,1,0.1);
-        an.start();
-	an = new animate.scale(app,target,1,1.5,0.3);
-        an.start();
+    var old = items[item];
+    item = item - 1;
+    if (item == -1) {
+	item = 0;
+	return;
+    }
+    
+    var target = items[item];
+
+    old.bbox.update();
+    target.bbox.update();
+    
+    var an = new animate.scale(app, old, 1, 1);
+    animate.run([an], 0, 0.1);
+    an = new animate.scale(app, target, 1, 1.5);
+    animate.run([an], 0, 0.3);
 });
 
 app.addKeyListener(mbapp.KEY_RIGHT, function() {
-	var old = items[item];
-	item = item + 1;
-	if (item == items.length) {
-		item = item - 1;
-		return;
-	}
-        var target = items[item];
-	var an = new animate.scale(app,old,1,1,0.1);
-        an.start();
-	an = new animate.scale(app,target,1,1.5,0.3);
-        an.start();
+    var old = items[item];
+    item = item + 1;
+    if (item == items.length) {
+	item = item - 1;
+	return;
+    }
+    
+    var target = items[item];
+
+    old.bbox.update();
+    target.bbox.update();
+    
+    var an = new animate.scale(app, old, 1, 1);
+    animate.run([an], 0, 0.1);
+    an = new animate.scale(app, target, 1, 1.5);
+    animate.run([an], 0, 0.3);
 });
 
 app.addKeyListener(mbapp.KEY_UP, function() {
-	var old = lines[line];
-	line = line - 1;
-	if (line == -1) {
-		line = 0;
-		return;
-	}
-        var target = lines[line];
-	var sy = target.y-lightbar.y;
-	sys.puts(sy);
-	var an = new animate.linear(app,lightbar,0,sy,0.3);
-        an.start();
+    var old = lines[line];
+    line = line - 1;
+    if (line == -1) {
+	line = 0;
+	return;
+    }
+    var target = lines[line];
+    var sy = target.center.y - lightbar.center.y;
+    sys.puts(sy);
+    var an = new animate.shift(app, lightbar, 0, sy);
+    animate.run([an], 0, 0.3);
 });
 app.addKeyListener(mbapp.KEY_DOWN, function() {
-	var old = lines[line];
-	line = line + 1;
-	if (line == lines.length) {
-		line = line - 1; 
-		return;
-	}
-        var target = lines[line];
-	var sy = target.y-lightbar.y;
-	sys.puts("line="+line);
-	sys.puts("sy="+sy);
-	sys.puts("target.y="+target.y);
-	sys.puts("lightbar.y="+lightbar.y);
-	var an = new animate.linear(app,lightbar,0,sy,0.3);
-        an.start();
+    var old = lines[line];
+    line = line + 1;
+    if (line == lines.length) {
+	line = line - 1; 
+	return;
+    }
+    var target = lines[line];
+    var sy = target.center.y - lightbar.center.y;
+    sys.puts("line="+line);
+    sys.puts("sy="+sy);
+    sys.puts("target.y="+target.center.y);
+    sys.puts("lightbar.y="+lightbar.center.y);
+    var an = new animate.shift(app, lightbar, 0, sy);
+    animate.run([an], 0, 0.3);
 });
 
 app.addKeyListener(mbapp.KEY_ENTER, function() {
-	var target = items[item];
-	var sx = 500-target.x;
-	var sy = 220-target.y;
-	sys.puts("target "+sx+','+sy);
-	var an = new animate.linear(app,target,sx,sy,1);
-	an.start();
-	for(i=0;i<items.length;i++) {
-	    if (i == item) continue;
-	    var x = Math.random();
-	    var y = Math.random();
-	    if (x > 0.5) x = 900;
-	    else x = -500;
-	    if (y > 0.5) y = 900;
-	    else y = -500;
-	    sx = x-items[i].x;
-	    sy = y-items[i].y;
-	    an = new animate.linear(app,items[i], sx,sy,2);
-	    an.start();
-	    alpha = new animate.alpha(app,items[i],0, 1);
-	    alpha.start();
-	}
+    var target = items[item];
+    var sx = 500 - target.center.x;
+    var sy = 220 - target.center.y;
+    sys.puts("target "+sx+','+sy);
+    var an = new animate.shift(app,target,sx,sy,1);
+    an.start();
+    for(i=0;i<items.length;i++) {
+	if (i == item) continue;
+	var x = Math.random();
+	var y = Math.random();
+	if (x > 0.5) x = 900;
+	else x = -500;
+	if (y > 0.5) y = 900;
+	else y = -500;
+	sx = x - items[i].center.x;
+	sy = y - items[i].center.y;
+	an = new animate.shift(app,items[i], sx, sy);
+	animate.run([an], 0, 2);
+	alpha = new animate.alpha(app,items[i], 0);
+	animate.run([an], 0, 1);
+    }
 });
 
 app.loop();
--- a/src/graph_engine_cairo.c	Sun Oct 03 18:12:11 2010 +0800
+++ b/src/graph_engine_cairo.c	Sun Oct 03 18:15:00 2010 +0800
@@ -19,7 +19,7 @@
  */
 static
 FcPattern *query_font_pattern(const char *family, int slant, int weight) {
-    FcPattern *ptn, *p, *fn_ptn;
+    FcPattern *ptn, *fn_ptn;
     FcValue val;
     FcConfig *cfg;
     FcBool r;
@@ -32,8 +32,7 @@
 
     cfg = FcConfigGetCurrent();
     ptn = FcPatternCreate();
-    p = FcPatternCreate();
-    if(ptn == NULL || p == NULL)
+    if(ptn == NULL)
 	goto err;
 
     val.type = FcTypeString;
@@ -48,17 +47,17 @@
     val.u.i = weight;
     FcPatternAdd(ptn, "weight", val, FcTrue);
 
-    r = FcConfigSubstituteWithPat(cfg, ptn, NULL, FcMatchPattern);
+    FcDefaultSubstitute(ptn);
+
+    r = FcConfigSubstituteWithPat(cfg, ptn, ptn, FcMatchPattern);
     if(!r)
 	goto err;
 
-    r = FcConfigSubstituteWithPat(cfg, p, ptn, FcMatchFont);
+    r = FcConfigSubstituteWithPat(cfg, ptn, ptn, FcMatchFont);
     if(!r)
 	goto err;
 
-    FcDefaultSubstitute(p);
-
-    fn_ptn = FcFontMatch(cfg, p, &result);
+    fn_ptn = FcFontMatch(cfg, ptn, &result);
 
     /* It is supposed to return FcResultMatch.  But, it is no, now.
      * I don't know why.  Someone should figure out the issue.
@@ -73,15 +72,12 @@
 	goto err;
 
     FcPatternDestroy(ptn);
-    FcPatternDestroy(p);
 
     return fn_ptn;
 
 err:
     if(ptn)
 	FcPatternDestroy(ptn);
-    if(p)
-	FcPatternDestroy(p);
     return NULL;
 
 }
--- a/src/paint.c	Sun Oct 03 18:12:11 2010 +0800
+++ b/src/paint.c	Sun Oct 03 18:15:00 2010 +0800
@@ -17,7 +17,7 @@
 int _paint_color_size = sizeof(paint_color_t);
 
 
-static void paint_color_prepare(paint_t *paint, mbe_t *cr) {
+static void paint_color_prepare(paint_t *paint, mbe_t *cr, shape_t *sh) {
     paint_color_t *color = (paint_color_t *)paint;
 
     mbe_set_source_rgba(cr, color->r, color->g, color->b, color->a);
@@ -84,17 +84,27 @@
 
 int _paint_linear_size = sizeof(paint_linear_t);
 
-static void paint_linear_prepare(paint_t *paint, mbe_t *cr) {
+static void paint_linear_prepare(paint_t *paint, mbe_t *cr, shape_t *sh) {
     paint_linear_t *linear = (paint_linear_t *)paint;
     mbe_pattern_t *ptn;
+    co_aix x1, y1;
+    co_aix x2, y2;
+    co_aix *mtx;
 
     ptn = linear->ptn;
     if(linear->flags & LIF_DIRTY) {
+	mtx = sh_get_aggr_matrix(sh);
+	x1 = linear->x1;
+	y1 = linear->y1;
+	matrix_trans_pos(mtx, &x1, &y1);
+	x2 = linear->x2;
+	y2 = linear->y2;
+	matrix_trans_pos(mtx, &x2, &y2);
+
 	if(ptn)
 	    mbe_pattern_destroy(ptn);
 	linear->flags &= ~LIF_DIRTY;
-	ptn = mbe_pattern_create_linear(linear->x1, linear->y1,
-					linear->x2, linear->y2,
+	ptn = mbe_pattern_create_linear(x1, y1, x2, y2,
 					linear->stops, linear->n_stops);
 	ASSERT(ptn != NULL);
 	linear->ptn = ptn;
@@ -174,13 +184,20 @@
 
 int _paint_radial_size = sizeof(paint_radial_t);
 
-static void paint_radial_prepare(paint_t *paint, mbe_t *cr) {
+static void paint_radial_prepare(paint_t *paint, mbe_t *cr, shape_t *sh) {
     paint_radial_t *radial = (paint_radial_t *)paint;
     mbe_pattern_t *ptn;
+    co_aix cx, cy;
+    co_aix *mtx;
 
     if(radial->flags & RDF_DIRTY) {
-	ptn = mbe_pattern_create_radial(radial->cx, radial->cy, 0,
-					  radial->cx, radial->cy,
+	mtx = sh_get_aggr_matrix(sh);
+	cx = radial->cx;
+	cy = radial->cy;
+	matrix_trans_pos(mtx, &cx, &cy);
+	
+	ptn = mbe_pattern_create_radial(cx, cy, 0,
+					cx, cy,
 					radial->r,
 					radial->stops,
 					radial->n_stops);
@@ -256,7 +273,7 @@
 int _paint_image_size = sizeof(paint_image_t);
 
 static
-void paint_image_prepare(paint_t *paint, mbe_t *cr) {
+void paint_image_prepare(paint_t *paint, mbe_t *cr, shape_t *sh) {
     paint_image_t *paint_img = (paint_image_t *)paint;
     mb_img_data_t *img_data;
 
--- a/src/redraw_man.c	Sun Oct 03 18:12:11 2010 +0800
+++ b/src/redraw_man.c	Sun Oct 03 18:15:00 2010 +0800
@@ -612,6 +612,7 @@
 coord_canvas_info_new(redraw_man_t *rdman, coord_t *coord,
 		      mbe_t *canvas) {
     coord_canvas_info_t *info;
+    static co_aix id[6] = {1, 0, 0, 0, 1, 0};
 
     info = (coord_canvas_info_t *)elmpool_elm_alloc(rdman->coord_canvas_pool);
     if(info == NULL)
@@ -624,6 +625,10 @@
     bzero(info->pcache_areas, sizeof(area_t) * 2);
     info->pcache_cur_area = &info->pcache_areas[0];
     info->pcache_last_area = &info->pcache_areas[1];
+    memcpy(info->cache_2_pdev, id, sizeof(co_aix) * 6);
+    memcpy(info->cache_2_pdev_rev, id, sizeof(co_aix) * 6);
+    memcpy(info->aggr_2_pdev, id, sizeof(co_aix) * 6);
+    memcpy(info->aggr_2_pdev_rev, id, sizeof(co_aix) * 6);
 
     return info;
 }
@@ -1310,8 +1315,8 @@
 
 /* \brief Compute matrix from cached canvas to parent device space.
  */
-static void compute_cached_2_pdev_matrix(coord_t *coord,
-					   co_aix canvas2pdev_matrix[6]) {
+static void compute_cached_2_pdev_matrix(coord_t *coord) {
+    co_aix *canvas2pdev_matrix = coord_get_2pdev(coord);
     coord_t *parent;
     co_aix *aggr;
     co_aix *matrix, *paggr;
@@ -1337,6 +1342,8 @@
     canvas2p[5] = shift_y;
 
     matrix_mul(paggr, canvas2p, canvas2pdev_matrix);
+
+    compute_reverse(canvas2pdev_matrix, coord_get_2pdev_rev(coord));
 }
 
 /*! \brief Compute area in parent cached coord for a cached coord.
@@ -1351,7 +1358,7 @@
  * ancestral cached coord.
  */
 static void compute_pcache_area(coord_t *coord) {
-    co_aix cached2pdev[6];
+    co_aix *cached2pdev = coord_get_2pdev(coord);
     int c_w, c_h;
     canvas_t *canvas;
     coord_canvas_info_t *canvas_info;
@@ -1360,7 +1367,6 @@
     canvas_info = coord->canvas_info;
     SWAP(canvas_info->pcache_cur_area, canvas_info->pcache_last_area,
 	 area_t *);
-    compute_cached_2_pdev_matrix(coord, cached2pdev);
 
     canvas = _coord_get_canvas(coord);
     canvas_get_size(canvas, &c_w, &c_h);
@@ -1766,6 +1772,7 @@
 	coord = all_zeroing->ds[i];
 	if(coord_is_zeroing(coord))
 	    zeroing_coord(rdman, coord);
+	compute_cached_2_pdev_matrix(coord);
 	/* This is required by ancester cached ones to perform
 	 * zeroing.
 	 */
@@ -1775,6 +1782,34 @@
     return OK;
 }
 
+/*! \brief Update aggregated cache_2_pdev matrix for cached coords.
+ *
+ * This is perfromed from root to leaves.  Aggregated cache_2_pdev is
+ * named as aggr_2_pdev field of canvas_info_t.  It is the matrix to
+ * transform a point from space of a cached coord to the space of root
+ * coord.
+ */
+static int
+update_aggr_pdev(redraw_man_t *rdman) {
+    int i;
+    coords_t *all_zeroing;
+    coord_t *coord, *parent_cached;
+
+    all_zeroing = &rdman->zeroing_coords;
+    for(i = 0; i < all_zeroing->num; i++) {
+	coord = all_zeroing->ds[i];
+	parent_cached = coord_get_cached(coord_get_parent(coord));
+	matrix_mul(coord_get_2pdev(parent_cached),
+		   coord_get_2pdev(coord),
+		   coord_get_aggr2pdev(coord));
+	matrix_mul(coord_get_2pdev_rev(coord),
+		   coord_get_2pdev_rev(parent_cached),
+		   coord_get_aggr2pdev_rev(coord));
+    }
+
+    return OK;
+}
+
 /*! \brief Add aggregated dirty areas to ancestor.
  *
  * Dirty areas are aggregated into two areas.  It assumes that even or odd
@@ -1786,7 +1821,7 @@
     int i;
     int n_areas;
     co_aix poses0[2][2], poses1[2][2];
-    co_aix canvas2pdev_matrix[6];
+    co_aix *canvas2pdev_matrix;
     area_t **areas, *area;
     area_t *area0, *area1;
     coord_t *parent, *pcached_coord;
@@ -1863,7 +1898,7 @@
     parent = coord_get_parent(coord);
     pcached_coord = coord_get_cached(parent);
 
-    compute_cached_2_pdev_matrix(coord, canvas2pdev_matrix);
+    canvas2pdev_matrix = coord_get_2pdev(coord);
 
     /* Add dirty areas to parent cached coord. */
     matrix_trans_pos(canvas2pdev_matrix, poses0[0], poses0[0] + 1);
@@ -2032,6 +2067,10 @@
     if(r != OK)
 	return ERR;
 
+    r = update_aggr_pdev(rdman);
+    if(r != OK)
+	return ERR;
+
     /*
      * Clear all flags setted by zeroing.
      */
@@ -2129,7 +2168,7 @@
 
 	fill = shape->fill;
 	if(shape->fill) {
-	    fill->prepare(fill, cr);
+	    fill->prepare(fill, cr, shape);
 	    if(shape->stroke)
 		fill_path_preserve(rdman, cr);
 	    else
@@ -2138,7 +2177,7 @@
 
 	stroke = shape->stroke;
 	if(stroke) {
-	    stroke->prepare(stroke, cr);
+	    stroke->prepare(stroke, cr, shape);
 	    set_shape_stroke_param(shape, cr);
 	    stroke_path(rdman, cr);
 	}
@@ -2209,22 +2248,20 @@
 static void update_cached_canvas_2_parent(redraw_man_t *rdman,
 					  coord_t *coord) {
     mbe_t *pcanvas, *canvas;
-    co_aix reverse[6];
-    co_aix canvas2pdev_matrix[6];
+    co_aix *c2pdev_reverse;
 
     if(coord_is_root(coord))
 	return;
 
-    compute_cached_2_pdev_matrix(coord, canvas2pdev_matrix);
-    compute_reverse(canvas2pdev_matrix, reverse);
+    c2pdev_reverse = coord_get_2pdev_rev(coord);
 
     canvas = _coord_get_canvas(coord);
     pcanvas = _coord_get_canvas(coord->parent);
 #ifndef UNITTEST
-    _update_cached_canvas_2_parent(rdman, reverse, canvas, pcanvas,
+    _update_cached_canvas_2_parent(rdman, c2pdev_reverse, canvas, pcanvas,
 				   coord->opacity);
 #else
-    memcpy(((mock_mbe_t *)canvas)->parent_2_cache, reverse,
+    memcpy(((mock_mbe_t *)canvas)->parent_2_cache, c2pdev_reverse,
 	   sizeof(co_aix) * 6);
 #endif
 }
@@ -2344,8 +2381,10 @@
 	draw_dirty_cached_coord(rdman, coord);
 	coord_set_flags(coord, COF_TEMP_MARK);
     }
-    for(i = 0; i < num; i++)
+    for(i = 0; i < num; i++) {
+	coord = zeroings[i];
 	coord_clear_flags(coord, COF_TEMP_MARK);
+    }
 
     draw_dirty_cached_coord(rdman, rdman->root_coord);
 }