gnash-commit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Gnash-commit] gnash backend/render_handler.h backend/render_h...


From: Udo Giacomozzi
Subject: [Gnash-commit] gnash backend/render_handler.h backend/render_h...
Date: Wed, 28 Feb 2007 17:25:26 +0000

CVSROOT:        /cvsroot/gnash
Module name:    gnash
Changes by:     Udo Giacomozzi <udog>   07/02/28 17:25:26

Modified files:
        backend        : render_handler.h render_handler_agg.cpp 
        gui            : fb.cpp fbsup.h gtk.cpp gtksup.h gui.cpp gui.h 
                         gtk_glue_agg.cpp 
        server         : button_character_instance.cpp 
                         button_character_instance.h character.cpp 
                         character.h dlist.cpp dlist.h 
                         edit_text_character.cpp edit_text_character.h 
                         generic_character.cpp generic_character.h 
                         movie_root.cpp movie_root.h render.cpp render.h 
                         sprite_instance.cpp sprite_instance.h 
                         video_stream_instance.cpp 
                         video_stream_instance.h 
        libgeometry    : Makefile.am 
        testsuite      : MovieTester.cpp 
        testsuite/misc-ming.all: loadMovieTestRunner.cpp 
Added files:
        libgeometry    : snappingrange.h 

Log message:
        lots of changes in favour of multiple ranges (see ChangeLog)

CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/gnash/backend/render_handler.h?cvsroot=gnash&r1=1.28&r2=1.29
http://cvs.savannah.gnu.org/viewcvs/gnash/backend/render_handler_agg.cpp?cvsroot=gnash&r1=1.61&r2=1.62
http://cvs.savannah.gnu.org/viewcvs/gnash/gui/fb.cpp?cvsroot=gnash&r1=1.26&r2=1.27
http://cvs.savannah.gnu.org/viewcvs/gnash/gui/fbsup.h?cvsroot=gnash&r1=1.16&r2=1.17
http://cvs.savannah.gnu.org/viewcvs/gnash/gui/gtk.cpp?cvsroot=gnash&r1=1.69&r2=1.70
http://cvs.savannah.gnu.org/viewcvs/gnash/gui/gtksup.h?cvsroot=gnash&r1=1.34&r2=1.35
http://cvs.savannah.gnu.org/viewcvs/gnash/gui/gui.cpp?cvsroot=gnash&r1=1.63&r2=1.64
http://cvs.savannah.gnu.org/viewcvs/gnash/gui/gui.h?cvsroot=gnash&r1=1.44&r2=1.45
http://cvs.savannah.gnu.org/viewcvs/gnash/gui/gtk_glue_agg.cpp?cvsroot=gnash&r1=1.12&r2=1.13
http://cvs.savannah.gnu.org/viewcvs/gnash/server/button_character_instance.cpp?cvsroot=gnash&r1=1.29&r2=1.30
http://cvs.savannah.gnu.org/viewcvs/gnash/server/button_character_instance.h?cvsroot=gnash&r1=1.12&r2=1.13
http://cvs.savannah.gnu.org/viewcvs/gnash/server/character.cpp?cvsroot=gnash&r1=1.21&r2=1.22
http://cvs.savannah.gnu.org/viewcvs/gnash/server/character.h?cvsroot=gnash&r1=1.51&r2=1.52
http://cvs.savannah.gnu.org/viewcvs/gnash/server/dlist.cpp?cvsroot=gnash&r1=1.50&r2=1.51
http://cvs.savannah.gnu.org/viewcvs/gnash/server/dlist.h?cvsroot=gnash&r1=1.28&r2=1.29
http://cvs.savannah.gnu.org/viewcvs/gnash/server/edit_text_character.cpp?cvsroot=gnash&r1=1.43&r2=1.44
http://cvs.savannah.gnu.org/viewcvs/gnash/server/edit_text_character.h?cvsroot=gnash&r1=1.22&r2=1.23
http://cvs.savannah.gnu.org/viewcvs/gnash/server/generic_character.cpp?cvsroot=gnash&r1=1.3&r2=1.4
http://cvs.savannah.gnu.org/viewcvs/gnash/server/generic_character.h?cvsroot=gnash&r1=1.18&r2=1.19
http://cvs.savannah.gnu.org/viewcvs/gnash/server/movie_root.cpp?cvsroot=gnash&r1=1.43&r2=1.44
http://cvs.savannah.gnu.org/viewcvs/gnash/server/movie_root.h?cvsroot=gnash&r1=1.39&r2=1.40
http://cvs.savannah.gnu.org/viewcvs/gnash/server/render.cpp?cvsroot=gnash&r1=1.14&r2=1.15
http://cvs.savannah.gnu.org/viewcvs/gnash/server/render.h?cvsroot=gnash&r1=1.14&r2=1.15
http://cvs.savannah.gnu.org/viewcvs/gnash/server/sprite_instance.cpp?cvsroot=gnash&r1=1.181&r2=1.182
http://cvs.savannah.gnu.org/viewcvs/gnash/server/sprite_instance.h?cvsroot=gnash&r1=1.72&r2=1.73
http://cvs.savannah.gnu.org/viewcvs/gnash/server/video_stream_instance.cpp?cvsroot=gnash&r1=1.9&r2=1.10
http://cvs.savannah.gnu.org/viewcvs/gnash/server/video_stream_instance.h?cvsroot=gnash&r1=1.7&r2=1.8
http://cvs.savannah.gnu.org/viewcvs/gnash/libgeometry/Makefile.am?cvsroot=gnash&r1=1.24&r2=1.25
http://cvs.savannah.gnu.org/viewcvs/gnash/libgeometry/snappingrange.h?cvsroot=gnash&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/gnash/testsuite/MovieTester.cpp?cvsroot=gnash&r1=1.22&r2=1.23
http://cvs.savannah.gnu.org/viewcvs/gnash/testsuite/misc-ming.all/loadMovieTestRunner.cpp?cvsroot=gnash&r1=1.3&r2=1.4

Patches:
Index: backend/render_handler.h
===================================================================
RCS file: /cvsroot/gnash/gnash/backend/render_handler.h,v
retrieving revision 1.28
retrieving revision 1.29
diff -u -b -r1.28 -r1.29
--- backend/render_handler.h    26 Jan 2007 13:31:29 -0000      1.28
+++ backend/render_handler.h    28 Feb 2007 17:25:25 -0000      1.29
@@ -17,7 +17,7 @@
 // 
 //
 
-/* $Id: render_handler.h,v 1.28 2007/01/26 13:31:29 strk Exp $ */
+/* $Id: render_handler.h,v 1.29 2007/02/28 17:25:25 udog Exp $ */
 
 #ifndef RENDER_HANDLER_H
 #define RENDER_HANDLER_H
@@ -164,6 +164,7 @@
 
 #include "shape_character_def.h"  
 #include "generic_character.h"    
+#include "Range2d.h"
 
 
 // Forward declarations.
@@ -276,9 +277,21 @@
                // implementation is optional    
        }
        
+       virtual void set_invalidated_regions(const InvalidatedRanges& 
/*ranges*/) {    
+               // implementation is optional    
+       }
+       
   /// Converts world coordinates to pixel coordinates
   virtual geometry::Range2d<int> world_to_pixel(const rect& worldbounds) = 0;
                
+  virtual geometry::Range2d<int> world_to_pixel(const 
geometry::Range2d<float>& worldbounds) {
+       if ((worldbounds.isNull() || worldbounds.isWorld()))    
+               return worldbounds;
+
+               return world_to_pixel(rect(worldbounds.getMinX(), 
worldbounds.getMinY(),
+                                          worldbounds.getMaxX(), 
worldbounds.getMaxY()));  
+       }
+               
        /// Bracket the displaying of a frame from a movie.
        //
        /// Fill the background color, and set up default
@@ -396,7 +409,19 @@
   ///
   /// See also gnash::renderer::bounds_in_clipping_area
   ///
-  virtual bool bounds_in_clipping_area(const rect& /*bounds*/) {
+  virtual bool bounds_in_clipping_area(const rect& bounds) {
+    return bounds_in_clipping_area(bounds.getRange());
+  }
+  
+  virtual bool bounds_in_clipping_area(const InvalidatedRanges& ranges) {
+       for (int rno=0; rno<ranges.size(); rno++) 
+               if (bounds_in_clipping_area(ranges.getRange(rno)))
+                       return true;
+                       
+       return false;
+       }
+  
+  virtual bool bounds_in_clipping_area(const geometry::Range2d<float>& 
/*bounds*/) {
     return true;
   }
 

Index: backend/render_handler_agg.cpp
===================================================================
RCS file: /cvsroot/gnash/gnash/backend/render_handler_agg.cpp,v
retrieving revision 1.61
retrieving revision 1.62
diff -u -b -r1.61 -r1.62
--- backend/render_handler_agg.cpp      23 Feb 2007 09:50:36 -0000      1.61
+++ backend/render_handler_agg.cpp      28 Feb 2007 17:25:25 -0000      1.62
@@ -16,7 +16,7 @@
 
  
 
-/* $Id: render_handler_agg.cpp,v 1.61 2007/02/23 09:50:36 udog Exp $ */
+/* $Id: render_handler_agg.cpp,v 1.62 2007/02/28 17:25:25 udog Exp $ */
 
 // Original version by Udo Giacomozzi and Hannes Mayr, 
 // INDUNET GmbH (www.indunet.it)
@@ -113,6 +113,8 @@
 #endif
 
 
+#include <vector>
+
 #include "gnash.h"
 #include "types.h"
 #include "image.h"
@@ -255,7 +257,7 @@
     delete [] m_buffer;
   }
   
-  void clear(geometry::Range2d<int> region)
+  void clear(const geometry::Range2d<int>& region)
   {
          if (region.isNull()) return;
          assert ( region.isFinite() );
@@ -314,6 +316,8 @@
 private:
   typedef agg::renderer_base<PixelFormat> renderer_base;
     
+  typedef agg::conv_stroke< agg::conv_curve< agg::path_storage > > stroke_type;
+  
   // TODO: Change these!!
        unsigned char *memaddr;
        int     memsize;
@@ -430,8 +434,8 @@
          point a;
          mat->transform(&a, point(bounds->get_x_min(), bounds->get_y_min()));
 
-          int xpos = round( TWIPS_TO_PIXELS(a.m_x) );
-         int ypos = round( TWIPS_TO_PIXELS(a.m_y) );
+    int xpos = (int)round( TWIPS_TO_PIXELS(a.m_x) );
+         int ypos = (int)round( TWIPS_TO_PIXELS(a.m_y) );
 
          // TODO: handle this by only blitting part of the source RGB image.
          if (xpos < 0) {
@@ -448,7 +452,7 @@
          // for performance purposes. Therefore, we need to use the
          // actual image size so we don't copy padding bytes to the Agg
          // buffer.
-         int frame_width = TWIPS_TO_PIXELS(bounds->width()) * bytes_per_pixel;
+         int frame_width = (int)TWIPS_TO_PIXELS(bounds->width()) * 
bytes_per_pixel;
          unsigned char* rgbbuf_ptr = frame->m_data;
          unsigned char* rgbbuf_end = rgbbuf_ptr + frame_width *
                                      frame->m_height;
@@ -524,8 +528,6 @@
     m_pixf = new PixelFormat(m_rbuf);
     //m_rbase = new renderer_base(*m_pixf);  --> does not work!!??
     
-    _clipbounds.setTo(0, 0, xres, yres);
-        
     log_msg("initialized AGG buffer <%p>, %d bytes, %dx%d, rowsize is %d 
bytes", 
       mem, size, x, y, row_size);
   }
@@ -552,8 +554,8 @@
          assert(m_pixf != NULL);
 
          // clear the stage using the background color
-    if ( ! _clipbounds.isNull() )    
-      clear_framebuffer(_clipbounds, agg::rgba8_pre(background_color.m_r,
+         for (unsigned int i=0; i<_clipbounds.size(); i++) 
+      clear_framebuffer(_clipbounds[i], agg::rgba8_pre(background_color.m_r,
                  background_color.m_g, background_color.m_b,
                  background_color.m_a));
          
@@ -572,10 +574,11 @@
        /// still correct, but slower. 
   /// This function clears only a certain portion of the screen, while /not/ 
   /// being notably slower for a fullscreen clear. 
-       void clear_framebuffer(geometry::Range2d<int> region,
+       void clear_framebuffer(const geometry::Range2d<int>& region,
                    agg::rgba8 color)
        {
-           unsigned int width = region.width();
+                       assert(region.isFinite());
+           unsigned int width = region.width()+1;
            if (width < 1)
            {
                log_warning("clear_framebuffer() called with width=%d",
@@ -589,11 +592,9 @@
                        region.height());
                return;
            }
-         
-           // to be exact, it's one off the max. (?)
            unsigned int left=region.getMinX();
            for (unsigned int y=region.getMinY(), maxy=region.getMaxY();
-                   y<maxy; ++y) 
+                   y<=maxy; ++y) 
            {
                m_pixf->copy_hline(left, y, width, color);
            }
@@ -649,28 +650,42 @@
        }
 
 
+       template <class ras_type>
+       void apply_clip_box(ras_type& ras, 
+               const geometry::Range2d<int>& bounds)
+       {
+               assert(bounds.isFinite());
+               ras.clip_box(
+                       (double)bounds.getMinX(),
+                       (double)bounds.getMinY(),
+                       (double)bounds.getMaxX()+1,
+                       (double)bounds.getMaxY()+1);  
+       }
+
 
   void draw_line_strip(const void* coords, int vertex_count, const rgba color)
        // Draw the line strip formed by the sequence of points.
        {
          assert(m_pixf != NULL);
          
-         if ( _clipbounds.isNull() ) return;
+         if ( _clipbounds.size()==0 ) return;
 
     point pnt;
     
     renderer_base rbase(*m_pixf);
 
+    typedef agg::rasterizer_scanline_aa<> ras_type;
+
+       ras_type ras;
        agg::scanline_p8 sl;
-       agg::rasterizer_scanline_aa<> ras;
        agg::renderer_scanline_aa_solid<
        agg::renderer_base<PixelFormat> > ren_sl(rbase);
        
-       ras.clip_box(
-               (double)_clipbounds.getMinX(),
-               (double)_clipbounds.getMinY(),
-               (double)_clipbounds.getMaxX(),
-               (double)_clipbounds.getMaxY());         
+               for (unsigned int cno=0; cno<_clipbounds.size(); cno++) {
+               
+                       const geometry::Range2d<int>& bounds = _clipbounds[cno];
+                                               
+                       apply_clip_box<ras_type> (ras, bounds);
 
     agg::path_storage path;
     agg::conv_stroke<agg::path_storage> stroke(path);
@@ -693,7 +708,10 @@
 
        // Set the color and render the scanlines
        ren_sl.color(agg::rgba8_pre(color.m_r, color.m_g, color.m_b, 
color.m_a));
+       
+       
        agg::render_scanlines(ras, sl, ren_sl);
+         }
 
        } // draw_line_strip
 
@@ -721,8 +739,8 @@
     
     agg_alpha_mask* new_mask = new agg_alpha_mask(xres, yres);
     
-    if ( ! _clipbounds.isNull() ) 
-      new_mask->clear(_clipbounds);
+    for (unsigned int cno=0; cno<_clipbounds.size(); cno++)  
+      new_mask->clear(_clipbounds[cno]);
     
     m_alpha_mask.push_back(new_mask);
     
@@ -750,24 +768,104 @@
       
     // NOTE: def->get_bound() is NULL for glyphs so we can't check the 
     // clipping area (bounds_in_clipping_area):
-      
     // create a new path with the matrix applied   
     std::vector<path> paths;    
     apply_matrix_to_path(def->get_paths(), paths, mat);
       
+    // convert to AGG paths
+    std::vector<agg::path_storage> agg_paths;
+    build_agg_paths(agg_paths, paths);
+    
     // make sure m_single_fill_styles contains the required color 
     need_single_fill_style(color);
 
+    // prepare style handler
+    agg_style_handler sh;
+    build_agg_styles(sh, m_single_fill_styles, mat, m_neutral_cxform);
+    
+    // select all clipping ranges. 
+    // NOTE: Glyphs are loaded like normal shape definitons, but w/o style
+    // definitons, which unfortunately include shape bounds. So "def" has
+    // no bounds (isNull) and thus select_clipbounds() won't work.
+    // TODO: Find a different solution since it's suboptimal to render in
+    // all clipping bounds.
+    select_all_clipbounds();
+      
     // draw the shape
     if (m_drawing_mask)
        draw_mask_shape(paths, false);
     else
-       draw_shape(-1, paths, m_single_fill_styles, m_neutral_cxform,  
-           mat, false);
+                       draw_shape(-1, paths, agg_paths, sh, false);
     
     // NOTE: Do not use even-odd filling rule for glyphs!
+    
+    // clear clipping ranges to ease debugging
+    _clipbounds_selected.clear();
+  }
+
+
+       /// Fills _clipbounds_selected with pointers to _clipbounds members who
+       /// intersect with the given character (transformed by mat). This avoids
+       /// rendering of characters outside a particular clipping range.
+       /// "_clipbounds_selected" is used by draw_shape() and draw_outline() 
and
+       /// *must* be initialized prior to using those function.
+       void select_clipbounds(const shape_character_def *def, const matrix& 
mat) {
+       
+               _clipbounds_selected.clear();
+               _clipbounds_selected.reserve(_clipbounds.size());
+               
+               rect ch_bounds = def->get_bound();
+
+               if (ch_bounds.is_null()) {
+                       log_msg("warning: select_clipbounds encountered a 
character definition "
+                               "with null bounds");
+                       return;
+               }               
+
+               rect bounds;            
+               bounds.set_null();
+               bounds.expand_to_transformed_rect(mat, ch_bounds);
+               bounds.scale_x(xscale);
+               bounds.scale_y(yscale);
+               
+               const geometry::Range2d<float>& range_float = bounds.getRange();
+               
+               assert(range_float.isFinite());
+               
+               geometry::Range2d<int> range_int(
+                 (int) range_float.getMinX(),
+                 (int) range_float.getMinY(),
+                 (int) range_float.getMaxX(),
+                 (int) range_float.getMaxY()
+               );
+               
+               
+               int count = _clipbounds.size();
+               for (int cno=0; cno<count; cno++) {
+                                       
+                       if (_clipbounds[cno].intersects(bounds.getRange())) 
+                               
_clipbounds_selected.push_back(&_clipbounds[cno]);
+
   }
 
+               /*      
+               printf("Selected %d out of %d bounds.\n", 
_clipbounds_selected.size(),
+                       _clipbounds.size());
+               */
+       }
+       
+       void select_all_clipbounds() {
+       
+               if (_clipbounds_selected.size() == _clipbounds.size()) 
+                       return; // already all selected
+       
+               _clipbounds_selected.clear();
+               _clipbounds_selected.resize(_clipbounds.size());
+               
+               int count = _clipbounds.size();
+               for (int cno=0; cno<count; cno++) 
+                       _clipbounds_selected[cno] = &_clipbounds[cno];
+       }
 
   void draw_shape_character(shape_character_def *def, 
     const matrix& mat,
@@ -776,9 +874,12 @@
     const std::vector<fill_style>& fill_styles,
     const std::vector<line_style>& line_styles) {
 
-    std::vector<path> paths;
+    std::vector< path > paths;
+    std::vector< agg::path_storage > agg_paths;
     
     apply_matrix_to_path(def->get_paths(), paths, mat);
+    build_agg_paths(agg_paths, paths);
+    
 
     if (m_drawing_mask) {
       
@@ -787,18 +888,37 @@
     
     } else {
       
-      // We need to separate sub-shapes during rendering. The current 
-      // implementation is a bit sub-optimal because the fill styles get
-      // re-initialized for each sub-shape. Maybe this will be no more a 
problem
-      // once fill styles get cached, anyway.     
+       // select ranges
+       select_clipbounds(def, mat);
+       
+       if (_clipbounds_selected.empty()) {
+               log_msg("warning: AGG renderer skipping a whole character");
+               return; // nothing to draw!?
+                       }
+    
+       // prepare fill styles
+       agg_style_handler sh;
+       build_agg_styles(sh, fill_styles, mat, cx);
+       
+       /*
+       // prepare strokes
+       std::vector<stroke_type*> strokes;
+       build_agg_strokes(strokes, agg_paths, paths, line_styles, mat);
+       */
+      
+      // We need to separate sub-shapes during rendering. 
       const int subshape_count=count_sub_shapes(paths);
       
       for (int subshape=0; subshape<subshape_count; subshape++) {
-        draw_shape(subshape, paths, fill_styles, cx, mat, true);    
-        draw_outlines(subshape, paths, line_styles, cx, mat);
+        draw_shape(subshape, paths, agg_paths, sh, true);    
+        draw_outlines(subshape, paths, agg_paths, line_styles, cx, mat);
       }
+      
     } // if not drawing mask
     
+    // Clear selected clipbounds to ease debugging 
+    _clipbounds_selected.clear();
+    
   } // draw_shape_character
 
 
@@ -876,97 +996,94 @@
   }
   
 
-  /// Draws the given path using the given fill style and color transform.
-  /// Normally, Flash shapes are drawn using even-odd filling rule. However,
-  /// for glyphs non-zero filling rule should be used (even_odd=0).
-  /// Note the paths have already been transformed by the matrix and 
-  /// 'fillstyle_matrix' is only provided for bitmap transformations.
-  /// 'subshape_id' defines which sub-shape should be drawn (-1 means all 
-  /// subshapes)  
-  void draw_shape(int subshape_id, const std::vector<path> &paths,
-    const std::vector<fill_style> &fill_styles, const cxform& cx,
-    const matrix& fillstyle_matrix, int even_odd) {
+  /// Transposes Gnash paths to AGG paths, which can be used for both outlines
+  /// and shapes. Subshapes are ignored (ie. all paths are converted). 
Converts 
+       /// TWIPS to pixels on the fly.
+  void build_agg_paths(std::vector<agg::path_storage>& dest, const 
std::vector<path>& paths) {
     
-    if (m_alpha_mask.empty()) {
+         int pcount = paths.size();
     
-      // No mask active, use normal scanline renderer
+               dest.resize(pcount);      
       
-      typedef agg::scanline_u8 scanline_type;
+         for (int pno=0; pno<pcount; pno++) {
       
-      scanline_type sl;
+               const gnash::path& this_path = paths[pno];
+                       agg::path_storage& new_path = dest[pno];
       
-      draw_shape_impl<scanline_type> (subshape_id, paths, fill_styles, cx, 
-        fillstyle_matrix, even_odd, sl);
+                       new_path.move_to(this_path.m_ax*xscale, 
this_path.m_ay*yscale);
         
-    } else {
+                       int ecount = this_path.m_edges.size();
     
-      // Mask is active, use alpha mask scanline renderer
+                       for (int eno=0; eno<ecount; eno++) {
       
-      typedef agg::scanline_u8_am<agg::alpha_mask_gray8> scanline_type;
+                               const edge& this_edge = this_path.m_edges[eno];
       
-      scanline_type sl(m_alpha_mask.back()->get_amask());
+        if (this_edge.is_straight())
+          new_path.line_to(this_edge.m_ax*xscale, this_edge.m_ay*yscale);
+        else
+          new_path.curve3(this_edge.m_cx*xscale, this_edge.m_cy*yscale,
+                      this_edge.m_ax*xscale, this_edge.m_ay*yscale);
       
-      draw_shape_impl<scanline_type> (subshape_id, paths, fill_styles, cx, 
-        fillstyle_matrix, even_odd, sl);
         
     }
     
   }
    
-  /// Template for draw_shape(). Two different scanline types are suppored, 
-  /// one with and one without an alpha mask. This makes drawing without masks
-  /// much faster.  
-  template <class scanline_type>
-  void draw_shape_impl(int subshape_id, const std::vector<path> &paths,
-    const std::vector<fill_style> &fill_styles, const cxform& cx,
-    const matrix& fillstyle_matrix, int even_odd, scanline_type& sl) {
-    /*
-    Fortunately, AGG provides a rasterizer that fits perfectly to the flash
-    data model. So we just have to feed AGG with all data and we're done. :-)
-    This is also far better than recomposing the polygons as the rasterizer
-    can do everything in one pass and it is also better for adjacent edges
-    (anti aliasing).
-    Thank to Maxim Shemanarev for providing us such a great tool with AGG...
-    */
+       } //build_agg_paths
     
-         assert(m_pixf != NULL);
          
-         assert(!m_drawing_mask);
+       // Builds vector strokes for paths
+       // WARNING 1 : This is not used and will probably be removed soon.
+       // WARNING 2 : Strokes vector returns pointers which are never freed.
+       void build_agg_strokes(std::vector<stroke_type*>& dest, 
+         std::vector<agg::path_storage>& agg_paths,
+         const std::vector<path> &paths,
+               const std::vector<line_style> &line_styles,
+               const matrix& linestyle_matrix) {
          
-         if ( _clipbounds.isNull() ) return;
+               assert(0); // should not be used currently
 
-    // Gnash stuff 
-    int pno, eno, fno;
-    int pcount, ecount, fcount;
+         int pcount=paths.size(); 
+         dest.resize(pcount);
     
-    // AGG stuff
-    renderer_base rbase(*m_pixf);
-    agg::rasterizer_scanline_aa<> ras;  // anti alias
-    agg::rasterizer_compound_aa<agg::rasterizer_sl_clip_dbl> rasc;  // 
flash-like renderer
-    agg::renderer_scanline_aa_solid<
-      agg::renderer_base<PixelFormat> > ren_sl(rbase); // solid fills
-    agg::span_allocator<agg::rgba8> alloc;  // span allocator (?)
-    agg_style_handler sh;               // holds fill style definitions
+    // use avg between x and y scale
+    const float stroke_scale = 
+      (linestyle_matrix.get_x_scale() + linestyle_matrix.get_y_scale()) / 2.0f
+      * (xscale+yscale)/2.0f;    
     
+         for (int pno=0; pno<pcount; pno++) {
 
-       rasc.clip_box(
-               (double)_clipbounds.getMinX(),
-               (double)_clipbounds.getMinY(),
-               (double)_clipbounds.getMaxX(),
-               (double)_clipbounds.getMaxY());         
+               agg::conv_curve<agg::path_storage> curve(agg_paths[pno]);
+               stroke_type* this_stroke = new stroke_type(curve);
     
-    // debug
-    int edge_count=0;
+               const gnash::path &this_path_gnash = paths[pno];
     
-    // activate even-odd filling rule
-    if (even_odd)
-      rasc.filling_rule(agg::fill_even_odd);
+      const line_style& lstyle = line_styles[this_path_gnash.m_line-1];
+               
+      int width = lstyle.get_width();
+      if (width==1)
+        this_stroke->width(1);
     else
-      rasc.filling_rule(agg::fill_non_zero);
+        this_stroke->width(width*stroke_scale);
+                        
+                       this_stroke->attach(curve);
+                       this_stroke->line_cap(agg::round_cap);
+                       this_stroke->line_join(agg::round_join);
       
-    // tell AGG what styles are used
-    fcount = fill_styles.size();
-    for (fno=0; fno<fcount; fno++) {
+                       dest[pno] = this_stroke;
+               
+               }
+         
+       }
+       
+       // Initializes the internal styles class for AGG renderer
+       void build_agg_styles(agg_style_handler& sh, 
+         const std::vector<fill_style>& fill_styles,
+               const matrix& fillstyle_matrix,
+               const cxform& cx) {
+         
+    int fcount = fill_styles.size();
+    for (int fno=0; fno<fcount; fno++) {
     
       bool smooth=false;
       int fill_type = fill_styles[fno].get_type();
@@ -1030,19 +1147,109 @@
         
     } // for
     
+       } //build_agg_styles
+  
+
+  /// Draws the given path using the given fill style and color transform.
+  /// Normally, Flash shapes are drawn using even-odd filling rule. However,
+  /// for glyphs non-zero filling rule should be used (even_odd=0).
+  /// Note the paths have already been transformed by the matrix and 
+  /// 'subshape_id' defines which sub-shape should be drawn (-1 means all 
+  /// subshapes).
+       /// Note the *coordinates* in "paths" are not used because they are 
+       /// already prepared in agg_paths. The (nearly ambiguous) "path" 
parameter
+       /// is used to access other properties like fill styles and subshapes.  
 
+  void draw_shape(int subshape_id, const std::vector<path> &paths,
+    const std::vector<agg::path_storage>& agg_paths,  
+    agg_style_handler& sh, int even_odd) {
+    
+    if (m_alpha_mask.empty()) {
+    
+      // No mask active, use normal scanline renderer
+      
+      typedef agg::scanline_u8 scanline_type;
+      
+      scanline_type sl;
+      
+      draw_shape_impl<scanline_type> (subshape_id, paths, agg_paths, 
+                         sh, even_odd, sl);
+        
+    } else {
+    
+      // Mask is active, use alpha mask scanline renderer
+      
+      typedef agg::scanline_u8_am<agg::alpha_mask_gray8> scanline_type;
+      
+      scanline_type sl(m_alpha_mask.back()->get_amask());
+      
+      draw_shape_impl<scanline_type> (subshape_id, paths, agg_paths, 
+                         sh, even_odd, sl);
+        
+    }
+    
+  }
+   
+  /// Template for draw_shape(). Two different scanline types are suppored, 
+  /// one with and one without an alpha mask. This makes drawing without masks
+  /// much faster.  
+  template <class scanline_type>
+  void draw_shape_impl(int subshape_id, const std::vector<path> &paths,
+       const std::vector<agg::path_storage>& agg_paths,
+    agg_style_handler& sh, int even_odd, scanline_type& sl) {
+    /*
+    Fortunately, AGG provides a rasterizer that fits perfectly to the flash
+    data model. So we just have to feed AGG with all data and we're done. :-)
+    This is also far better than recomposing the polygons as the rasterizer
+    can do everything in one pass and it is also better for adjacent edges
+    (anti aliasing).
+    Thank to Maxim Shemanarev for providing us such a great tool with AGG...
+    */
+    
+         assert(m_pixf != NULL);
+         
+         assert(!m_drawing_mask);
+         
+         if ( _clipbounds.size()==0 ) return;
+
+    // Gnash stuff 
+    int pno;
+    int pcount;
+    
+    // AGG stuff
+    typedef agg::rasterizer_compound_aa<agg::rasterizer_sl_clip_dbl> ras_type;
+    renderer_base rbase(*m_pixf);
+    ras_type rasc;  // flash-like renderer
+    agg::renderer_scanline_aa_solid<
+      agg::renderer_base<PixelFormat> > ren_sl(rbase); // solid fills
+    agg::span_allocator<agg::rgba8> alloc;  // span allocator (?)
+    
+
+    // activate even-odd filling rule
+    if (even_odd)
+      rasc.filling_rule(agg::fill_even_odd);
+    else
+      rasc.filling_rule(agg::fill_non_zero);
+      
+    
+               for (unsigned int cno=0; cno<_clipbounds_selected.size(); 
cno++) {
+               
+                       const geometry::Range2d<int>* bounds = 
_clipbounds_selected[cno];
+                       
+                       apply_clip_box<ras_type> (rasc, *bounds);
+                       
+                       int current_subshape=0;
       
     // push paths to AGG
     pcount = paths.size();
-    int current_subshape = 0; 
-    agg::path_storage path;
-    agg::conv_curve< agg::path_storage > curve(path);
 
     for (pno=0; pno<pcount; pno++) {
     
-      const gnash::path &this_path = paths[pno];
-      path.remove_all();
+               const gnash::path &this_path_gnash = paths[pno];
+             agg::path_storage &this_path_agg = 
+                                 
const_cast<agg::path_storage&>(agg_paths[pno]);
+             agg::conv_curve< agg::path_storage > curve(this_path_agg);        
      
       
-      if (this_path.m_new_shape) 
+               if (this_path_gnash.m_new_shape)
         current_subshape++;
         
       if ((subshape_id>=0) && (current_subshape!=subshape_id)) {
@@ -1050,33 +1257,17 @@
         continue;
       }
 
-      if ((this_path.m_fill0==0) && (this_path.m_fill1==0)) {
+                               if ((this_path_gnash.m_fill0==0) && 
(this_path_gnash.m_fill1==0)) {
         // Skip this path as it contains no fill style
         continue;
       }
       
+             
       // Tell the rasterizer which styles the following path will use.
       // The good thing is, that it already supports two fill styles out of
       // the box. 
       // Flash uses value "0" for "no fill", whereas AGG uses "-1" for that. 
-      rasc.styles(this_path.m_fill0-1, this_path.m_fill1-1);
-      
-      // starting point of path
-      path.move_to(this_path.m_ax*xscale, this_path.m_ay*yscale);
-      
-      ecount = this_path.m_edges.size();
-      edge_count += ecount;
-      for (eno=0; eno<ecount; eno++) {
-      
-        const edge &this_edge = this_path.m_edges[eno];
-
-        if (this_edge.is_straight())
-          path.line_to(this_edge.m_ax*xscale, this_edge.m_ay*yscale);
-        else
-          path.curve3(this_edge.m_cx*xscale, this_edge.m_cy*yscale,
-                      this_edge.m_ax*xscale, this_edge.m_ay*yscale);
-        
-      }
+             rasc.styles(this_path_gnash.m_fill0-1, this_path_gnash.m_fill1-1);
       
       // add path to the compound rasterizer
       rasc.add_path(curve);
@@ -1084,10 +1275,11 @@
     }
     //log_msg("%d edges\n", edge_count);
     
-    // render!
+                                       
     agg::render_scanlines_compound_layered(rasc, sl, rbase, alloc, sh);
+         }
     
-  } // draw_shape
+  } // draw_shape_impl
 
 
 
@@ -1211,6 +1403,7 @@
 
   /// Just like draw_shapes() except that it draws an outline.
   void draw_outlines(int subshape_id, const std::vector<path> &paths,
+       const std::vector<agg::path_storage>& agg_paths,
     const std::vector<line_style> &line_styles, const cxform& cx,
     const matrix& linestyle_matrix) {
     
@@ -1222,8 +1415,8 @@
       
       scanline_type sl;
       
-      draw_outlines_impl<scanline_type> (subshape_id, paths, line_styles, 
-        cx, linestyle_matrix, sl);
+      draw_outlines_impl<scanline_type> (subshape_id, paths, agg_paths, 
+                         line_styles, cx, linestyle_matrix, sl);
         
     } else {
     
@@ -1233,17 +1426,18 @@
       
       scanline_type sl(m_alpha_mask.back()->get_amask());
       
-      draw_outlines_impl<scanline_type> (subshape_id, paths, line_styles, 
-        cx, linestyle_matrix, sl);
+      draw_outlines_impl<scanline_type> (subshape_id, paths, agg_paths,
+                         line_styles, cx, linestyle_matrix, sl);
         
     }
     
-  }
+  } //draw_outlines
 
 
   /// Template for draw_outlines(), see draw_shapes_impl().
   template <class scanline_type>
   void draw_outlines_impl(int subshape_id, const std::vector<path> &paths,
+    const std::vector<agg::path_storage>& agg_paths,
     const std::vector<line_style> &line_styles, const cxform& cx, 
     const matrix& linestyle_matrix, scanline_type& sl) {
     
@@ -1252,15 +1446,15 @@
          if (m_drawing_mask)    // Flash ignores lines in mask /definitions/
       return;    
     
-    if ( _clipbounds.isNull() ) return;
+    if ( _clipbounds.size()==0 ) return;
 
     // TODO: While walking the paths for filling them, remember when a path
     // has a line style associated, so that we avoid walking the paths again
     // when there really are no outlines to draw...
     
     // Gnash stuff    
-    int pno, eno;
-    int pcount, ecount;
+    int pno;
+    int pcount;
     
     // use avg between x and y scale
     const float stroke_scale = 
@@ -1269,34 +1463,29 @@
     
     
     // AGG stuff
+    typedef agg::rasterizer_scanline_aa<> ras_type; 
+    ras_type ras;  // anti alias
     renderer_base rbase(*m_pixf);
-    agg::rasterizer_scanline_aa<> ras;  // anti alias
     agg::renderer_scanline_aa_solid<
       agg::renderer_base<PixelFormat> > ren_sl(rbase); // solid fills
-    agg::path_storage agg_path;             // a path in the AGG world
 
-        // TODO: what do do if _clipbox.isNull() or _clipbox.isWorld() ?
-       //       currently an assertion will fail when get{Min,Max}{X,Y}
-       //       are called below
 
-       ras.clip_box(
-               (double)_clipbounds.getMinX(),
-               (double)_clipbounds.getMinY(),
-               (double)_clipbounds.getMaxX(),
-               (double)_clipbounds.getMaxY());         
+               for (unsigned int cno=0; cno<_clipbounds_selected.size(); 
cno++) {
 
-    agg::conv_curve< agg::path_storage > curve(agg_path);    // to render 
curves
-    agg::conv_stroke< agg::conv_curve < agg::path_storage > > 
-      stroke(curve);  // to get an outline
+                       const geometry::Range2d<int>* bounds = 
_clipbounds_selected[cno];
+                               
+                       apply_clip_box<ras_type> (ras, *bounds);
     
+                       int current_subshape=0;
     
-    int current_subshape = 0; 
     pcount = paths.size();   
     for (pno=0; pno<pcount; pno++) {
       
-      const path &this_path = paths[pno];
+             const gnash::path& this_path_gnash = paths[pno];
+             agg::path_storage &this_path_agg = 
+                                 
const_cast<agg::path_storage&>(agg_paths[pno]);
       
-      if (this_path.m_new_shape)
+               if (this_path_gnash.m_new_shape)
         current_subshape++;
         
       if ((subshape_id>=0) && (current_subshape!=subshape_id)) {
@@ -1304,48 +1493,40 @@
         continue;
       }
       
-      if (!this_path.m_line)  
-        continue;     // invisible line
+                               if (this_path_gnash.m_line==0) {
+                                 // Skip this path as it contains no line style
+                                 continue;
+                               } 
                
+             agg::conv_curve< agg::path_storage > curve(this_path_agg); // to 
render curves
+                   agg::conv_stroke< agg::conv_curve < agg::path_storage > > 
+                     stroke(curve);  // to get an outline
+             
+             const line_style& lstyle = line_styles[this_path_gnash.m_line-1];
         
-      const line_style &lstyle = line_styles[this_path.m_line-1];
-      rgba color = cx.transform(lstyle.get_color());
       int width = lstyle.get_width();
       if (width==1)
         stroke.width(1);
       else
         stroke.width(width*stroke_scale);
+               
       stroke.line_cap(agg::round_cap);
       stroke.line_join(agg::round_join);
 
-        
-      agg_path.remove_all();  // clear path
-      
-      agg_path.move_to(this_path.m_ax*xscale, this_path.m_ay*yscale);
-        
-      ecount = this_path.m_edges.size();
-      for (eno=0; eno<ecount; eno++) {
-      
-        const edge &this_edge = this_path.m_edges[eno];
-        
-        if (this_edge.is_straight())
-          agg_path.line_to(this_edge.m_ax*xscale, this_edge.m_ay*yscale);
-        else
-          agg_path.curve3(this_edge.m_cx*yscale, this_edge.m_cy*yscale,
-                      this_edge.m_ax*yscale, this_edge.m_ay*yscale);
-        
-      } // for edges
-      
-      
+             ras.reset();
       ras.add_path(stroke);
+             
+             rgba color = cx.transform(lstyle.get_color());
       ren_sl.color(agg::rgba8_pre(color.m_r, color.m_g, color.m_b, color.m_a));
       
       agg::render_scanlines(ras, sl, ren_sl);
     
+           }
+    
     
     }
       
-  } // draw_outlines
+  } // draw_outlines_impl
 
 
   
@@ -1357,26 +1538,27 @@
 
     if (corner_count<1) return;
     
-    if ( _clipbounds.isNull() ) return;
+    if ( _clipbounds.size()==0 ) return;
     
     // TODO: Use aliased scanline renderer instead of anti-aliased one since
     // it is undesired anyway.
+    typedef agg::rasterizer_scanline_aa<> ras_type;
     renderer_base rbase(*m_pixf);
     agg::scanline_p8 sl;
-    agg::rasterizer_scanline_aa<> ras;
+    ras_type ras;
     agg::renderer_scanline_aa_solid<
       agg::renderer_base<PixelFormat> > ren_sl(rbase);
 
+               for (unsigned int cno=0; cno<_clipbounds.size(); cno++) {
+               
+                       const geometry::Range2d<int>& bounds = _clipbounds[cno];
+                          
+                       apply_clip_box<ras_type> (ras, bounds); 
+
         // TODO: what do do if _clipbox.isNull() or _clipbox.isWorld() ?
        //       currently an assertion will fail when get{Min,Max}{X,Y}
        //       are called below
 
-       ras.clip_box(
-               (double)_clipbounds.getMinX(),
-               (double)_clipbounds.getMinY(),
-               (double)_clipbounds.getMaxX(),
-               (double)_clipbounds.getMaxY());         
-      
     agg::path_storage path;
     point pnt, origin;
     
@@ -1401,6 +1583,7 @@
     if (fill.m_a>0) {
       ras.add_path(path);
       ren_sl.color(agg::rgba8_pre(fill.m_r, fill.m_g, fill.m_b, fill.m_a));
+             
       agg::render_scanlines(ras, sl, ren_sl);
     }
     
@@ -1413,8 +1596,10 @@
       ren_sl.color(agg::rgba8_pre(outline.m_r, outline.m_g, outline.m_b, 
outline.m_a));
       
       ras.add_path(stroke);
+             
       agg::render_scanlines(ras, sl, ren_sl);
     }
+    }
     
   }
                       
@@ -1444,31 +1629,72 @@
     return Range2d<int>(xmin, ymin, xmax, ymax);
   }
   
+  geometry::Range2d<int> world_to_pixel(const geometry::Range2d<float>& wb)
+  {
+       if (wb.isNull() || wb.isWorld()) return wb;
+       
+       int xmin, ymin, xmax, ymax;
+
+    world_to_pixel(xmin, ymin, wb.getMinX(), wb.getMinY());
+    world_to_pixel(xmax, ymax, wb.getMaxX(), wb.getMaxY());
+
+    return geometry::Range2d<int>(xmin, ymin, xmax, ymax);
+       }
   
   virtual void set_invalidated_region(const rect& bounds) {
   
+               // NOTE: Both single and multi ranges are supported by AGG 
renderer.
+               
+               InvalidatedRanges ranges;
+               ranges.add(bounds.getRange());
+               set_invalidated_regions(ranges);
+  
+  }
+         
+       virtual void set_invalidated_regions(const InvalidatedRanges& ranges) {
       using gnash::geometry::Range2d;
       
-      Range2d<int> pixbounds = world_to_pixel(bounds);
+               int count=0;
       
-      // add 2 pixels (GUI does that too)
-      pixbounds.growBy(2);
+               _clipbounds_selected.clear();
+               _clipbounds.clear();    
   
       // TODO: cache 'visiblerect' and maintain in sync with
       //       xres/yres.
       Range2d<int> visiblerect(0, 0, xres, yres);
-      _clipbounds = Intersection(pixbounds, visiblerect);
 
+               for (int rno=0; rno<ranges.size(); rno++) {
+               
+                       const Range2d<float>& range = ranges.getRange(rno);
+
+           Range2d<int> pixbounds = world_to_pixel(range);
+           
+           geometry::Range2d<int> bounds = Intersection(pixbounds, 
visiblerect);
+           
+           if (bounds.isNull()) continue; // out of screen
+           
+           assert(bounds.isFinite());
+           
+           _clipbounds.push_back(bounds);
+           
+           count++;
+         }
+         //log_msg("%d inv. bounds in frame", count);
   
   }
   
-  virtual bool bounds_in_clipping_area(const rect& bounds) {    
-    int bxmin, bxmax, bymin, bymax;
+  
+  virtual bool bounds_in_clipping_area(const geometry::Range2d<float>& bounds) 
{    
     
        using gnash::geometry::Range2d;
 
        Range2d<int> pixbounds = world_to_pixel(bounds);
-       return Intersect(pixbounds, _clipbounds);
+               
+               for (unsigned int cno=0; cno<_clipbounds.size(); cno++) {  
+                       if (Intersect(pixbounds, _clipbounds[cno]))
+                               return true;
+               }
+               return false;
   }
 
   void get_pixel(rgba& color_return, float world_x, float world_y) {
@@ -1515,7 +1741,8 @@
   PixelFormat *m_pixf;
   
   /// clipping rectangle
-  geometry::Range2d<int> _clipbounds;
+  std::vector< geometry::Range2d<int> > _clipbounds;
+  std::vector< geometry::Range2d<int>* > _clipbounds_selected;
   
   // this flag is set while a mask is drawn
   bool m_drawing_mask; 

Index: gui/fb.cpp
===================================================================
RCS file: /cvsroot/gnash/gnash/gui/fb.cpp,v
retrieving revision 1.26
retrieving revision 1.27
diff -u -b -r1.26 -r1.27
--- gui/fb.cpp  22 Feb 2007 14:04:42 -0000      1.26
+++ gui/fb.cpp  28 Feb 2007 17:25:25 -0000      1.27
@@ -41,7 +41,7 @@
 #include "render_handler.h"
 #include "render_handler_agg.h"
 
-//#define DEBUG_SHOW_FPS  // prints number of frames per second to STDOUT
+#define DEBUG_SHOW_FPS  // prints number of frames per second to STDOUT
 
 #ifdef DEBUG_SHOW_FPS
 # include <sys/types.h>
@@ -365,9 +365,7 @@
 
 void FBGui::renderBuffer()
 {
-       if ( _drawbounds.isNull() ) return; // nothing to do..
-
-       assert ( ! _drawbounds.isWorld() );
+       if ( _drawbounds.size() == 0 ) return; // nothing to do..
 
 #ifdef DOUBLE_BUFFER
 
@@ -379,24 +377,29 @@
   const unsigned int scanline_size =
     var_screeninfo.xres * pixel_size;
     
+    
+  for (int bno=0; bno < _drawbounds.size(); bno++) {
+       
+               geometry::Range2d<int>& bounds = _drawbounds[bno];
+               
+               assert ( ! bounds.isWorld() );  
+    
   // Size, in bytes, of a row that has to be copied
-  const unsigned int row_size = _drawbounds.width() * pixel_size;
+         const unsigned int row_size = (bounds.width()+1) * pixel_size;
     
   // copy each row
-  const int minx = _drawbounds.getMinX();
-  const int maxy = _drawbounds.getMaxY();
+         const int minx = bounds.getMinX();
+         const int maxy = bounds.getMaxY();
 
-  for (int y=_drawbounds.getMinY(); y<maxy; ++y) {
+         for (int y=bounds.getMinY(); y<=maxy; ++y) {
   
     const unsigned int pixel_index = y * scanline_size + minx*pixel_size;
     
     memcpy(&fbmem[pixel_index], &buffer[pixel_index], row_size);
     
   }  
+       }  
      
-
-       /*memcpy(fbmem, buffer, var_screeninfo.xres*var_screeninfo.yres*
-    (var_screeninfo.bits_per_pixel/8));*/
 #endif
        
 #ifdef DEBUG_SHOW_FPS
@@ -446,24 +449,27 @@
   return y;
 }
 
-void FBGui::setInvalidatedRegion(const rect& bounds) 
+void FBGui::setInvalidatedRegions(const InvalidatedRanges& ranges)
 {
+       _renderer->set_invalidated_regions(ranges);
 
-#ifdef DOUBLE_BUFFER
+       _drawbounds.clear();
   
-       // forward to renderer
-       _renderer->set_invalidated_region(bounds);
+       for (int rno=0; rno<ranges.size(); rno++) {
 
-       // update _drawbounds, which are the bounds that need to
-       // be rerendered
-       //      
-       _drawbounds = Intersection(
-    _renderer->world_to_pixel(bounds).growBy(2), // add two pixels because of 
anti-aliasing...
+               geometry::Range2d<int> bounds = Intersection(
+           _renderer->world_to_pixel(ranges.getRange(rno)),
     _validbounds);
 
-#endif
+               // it may happen that a particular range is out of the screen, 
which 
+               // will lead to bounds==null. 
+               if (bounds.isNull()) continue; 
+               
+               _drawbounds.push_back(bounds);
   
-}  // setInvalidatedRegion
+       }
+       
+}
 
 void FBGui::disable_terminal() 
 {

Index: gui/fbsup.h
===================================================================
RCS file: /cvsroot/gnash/gnash/gui/fbsup.h,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -b -r1.16 -r1.17
--- gui/fbsup.h 19 Feb 2007 18:15:35 -0000      1.16
+++ gui/fbsup.h 28 Feb 2007 17:25:25 -0000      1.17
@@ -24,6 +24,8 @@
 #include "config.h"
 #endif
 
+#include <vector>
+
 #include "gui.h"
 #include <linux/fb.h>
 
@@ -42,11 +44,11 @@
 
 // Define this to read from /dev/input/mice (any PS/2 compatbile mouse or
 // emulated by the Kernel) 
-#define USE_MOUSE_PS2
+//#define USE_MOUSE_PS2
 
 // Define this to support eTurboTouch / eGalax touchscreens. When reading from
 // a serial device, it must be initialized (stty) externally. 
-//#define USE_MOUSE_ETT
+#define USE_MOUSE_ETT
 
 #ifdef USE_MOUSE_PS2
 #define MOUSE_DEVICE "/dev/input/mice"
@@ -110,7 +112,7 @@
                unsigned char *buffer; // offscreen buffer
 #endif         
 
-    geometry::Range2d<int> _drawbounds;
+    std::vector< geometry::Range2d<int> > _drawbounds;
 
     int m_stage_width;
     int m_stage_height;
@@ -167,7 +169,8 @@
     virtual void setInterval(unsigned int interval);
     virtual void setTimeout(unsigned int timeout);
     
-    virtual void setInvalidatedRegion(const rect& bounds);
+    virtual void setInvalidatedRegions(const InvalidatedRanges& ranges);
+    virtual bool want_multiple_regions() { return true; }
 };
 
 // end of namespace gnash

Index: gui/gtk.cpp
===================================================================
RCS file: /cvsroot/gnash/gnash/gui/gtk.cpp,v
retrieving revision 1.69
retrieving revision 1.70
diff -u -b -r1.69 -r1.70
--- gui/gtk.cpp 28 Feb 2007 04:27:59 -0000      1.69
+++ gui/gtk.cpp 28 Feb 2007 17:25:25 -0000      1.70
@@ -17,7 +17,7 @@
 // 
 //
 
-/* $Id: gtk.cpp,v 1.69 2007/02/28 04:27:59 nihilus Exp $ */
+/* $Id: gtk.cpp,v 1.70 2007/02/28 17:25:25 udog Exp $ */
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
@@ -61,8 +61,7 @@
 
 GtkGui::GtkGui(unsigned long xid, float scale, bool loop, unsigned int depth)
        :
-       Gui(xid, scale, loop, depth),
-       _drawbounds(0, 0, 0, 0)
+       Gui(xid, scale, loop, depth)
 {
 }
 
@@ -235,7 +234,6 @@
        _height = height;
 
        _validbounds.setTo(0, 0, _width, _height);
-       _drawbounds = _validbounds;
     
        glue.setRenderHandlerSize(_width, _height);
 
@@ -245,16 +243,24 @@
 void
 GtkGui::renderBuffer()
 {
-       if ( _drawbounds.isNull() ) return;
-       assert ( ! _drawbounds.isWorld() );
-       glue.render(_drawbounds.getMinX(), _drawbounds.getMinY(),
-               _drawbounds.getMaxX(), _drawbounds.getMaxY());
+  if ( _drawbounds.size() == 0 ) return; // nothing to do..
+
+       for (int bno=0; bno < _drawbounds.size(); bno++) {
+       
+               geometry::Range2d<int>& bounds = _drawbounds[bno];
+               
+               assert ( bounds.isFinite() );  
+               
+               glue.render(bounds.getMinX(), bounds.getMinY(),
+                 bounds.getMaxX(), bounds.getMaxY());
+       
+       }
 }
 
+#ifdef RENDERER_AGG
 void
-GtkGui::setInvalidatedRegion(const rect& bounds)
+GtkGui::setInvalidatedRegions(const InvalidatedRanges& ranges)
 {
-#ifdef RENDERER_AGG
        // forward to renderer
        //
        // Why? Why have the region been invalidated ??
@@ -266,18 +272,28 @@
        // To be safe just assume this 'invalidated' region is actually
        // the offscreen buffer, for safety, but we need to clarify this.
        //
-       _renderer->set_invalidated_region(bounds);
+       _renderer->set_invalidated_regions(ranges);
 
-       // update _drawbounds, which are the bounds that need to
-       // be rerendered (??)
-       //
-       _drawbounds = Intersection(
-                       // add two pixels because of anti-aliasing...
-                       _renderer->world_to_pixel(bounds).growBy(2),
+       _drawbounds.clear();
+               
+       for (int rno=0; rno<ranges.size(); rno++) {
+       
+               geometry::Range2d<int> bounds = Intersection(
+           _renderer->world_to_pixel(ranges.getRange(rno)),
                        _validbounds);
 
-#endif
+               // it may happen that a particular range is out of the screen, 
which 
+               // will lead to bounds==null. 
+               if (bounds.isNull()) continue;
+               
+               assert(bounds.isFinite()); 
+               
+               _drawbounds.push_back(bounds);
+           
+       }
+
 }
+#endif
 
 void
 GtkGui::setTimeout(unsigned int timeout)

Index: gui/gtksup.h
===================================================================
RCS file: /cvsroot/gnash/gnash/gui/gtksup.h,v
retrieving revision 1.34
retrieving revision 1.35
diff -u -b -r1.34 -r1.35
--- gui/gtksup.h        31 Jan 2007 15:24:13 -0000      1.34
+++ gui/gtksup.h        28 Feb 2007 17:25:25 -0000      1.35
@@ -131,7 +131,10 @@
 
     GdkPixbuf* create_pixbuf(const gchar *filename);
     
-    void setInvalidatedRegion(const rect& bounds);
+#ifdef RENDERER_AGG    
+    void setInvalidatedRegions(const InvalidatedRanges& ranges);
+    bool want_multiple_regions() { return true; }
+#endif    
 
     virtual void setCursor(gnash_cursor_type newcursor);
     
@@ -142,7 +145,8 @@
     GtkMenu     *_popup_menu;
     GtkWidget   *_menubar;
     GtkWidget   *_vbox;
-    geometry::Range2d<int> _drawbounds;
+    //geometry::Range2d<int> _drawbounds;
+    std::vector< geometry::Range2d<int> > _drawbounds;
 
 #ifdef RENDERER_CAIRO
     cairo_t     *_cairo_handle;

Index: gui/gui.cpp
===================================================================
RCS file: /cvsroot/gnash/gnash/gui/gui.cpp,v
retrieving revision 1.63
retrieving revision 1.64
diff -u -b -r1.63 -r1.64
--- gui/gui.cpp 27 Feb 2007 09:54:49 -0000      1.63
+++ gui/gui.cpp 28 Feb 2007 17:25:25 -0000      1.64
@@ -46,7 +46,7 @@
 /// debug the GUI part, however (see if blitting the region works), then you 
 /// probably won't define this.
 #ifdef ENABLE_REGION_UPDATES_DEBUGGING 
-#define REGION_UPDATES_DEBUGGING_FULL_REDRAW 1
+//#define REGION_UPDATES_DEBUGGING_FULL_REDRAW 1
 #endif 
 
 #ifdef ENABLE_REGION_UPDATES_DEBUGGING
@@ -275,7 +275,7 @@
 Gui::display(movie_root* m)
 {
 
-       rect changed_bounds;  // area of the stage that must be updated 
+       InvalidatedRanges changed_ranges;
        bool redraw_flag;
 
        // Should the frame be rendered completely, even if it did not change?
@@ -289,18 +289,34 @@
        // due to ActionScript code, the timeline or user events. The GUI can 
still
        // choose to render a different part of the stage. 
        //
-       // TODO: is it needed to call get_invalidated_bounds even when 
redraw_flag is true ??
-       //
-       m->get_invalidated_bounds(&changed_bounds, false);
+       if (!redraw_flag) {
+               
+               // Choose distance (note these are TWIPS!) 
+               // 10% of normalized stage size
+               changed_ranges.snap_distance = sqrt(
+                 m->get_movie_definition()->get_width_pixels() * 20.0 * 
+                       m->get_movie_definition()->get_height_pixels() * 20.0) 
* 0.10;
+                       
+               // Use multi ranges only when GUI/Renderer supports it
+               // (Useless CPU overhead, otherwise)
+               changed_ranges.single_mode = !want_multiple_regions();
+
+               // scan through all sprites to compute invalidated bounds  
+               m->add_invalidated_bounds(changed_ranges, false);
+               
+               // optimize ranges
+               changed_ranges.combine_ranges();
+               
+       }
 
        if (redraw_flag)     // TODO: Remove this and want_redraw to avoid 
confusion!?
        {
-               changed_bounds.set_world();
+               changed_ranges.setWorld();
        }
   
        // Avoid drawing of stopped movies
 
-       if ( ! changed_bounds.is_null() ) // use 'else'?
+       if ( ! changed_ranges.isNull() ) // use 'else'?
        {
                // Tell the GUI(!) that we only need to update this
                // region. Note the GUI can do whatever it wants with
@@ -313,10 +329,11 @@
                // redraw the full screen so that only the
                // *new* invalidated region is visible
                // (helps debugging)
-               rect worldregion; worldregion.set_world();
-               setInvalidatedRegion(worldregion);
+               InvalidatedRanges world_ranges;
+               world_ranges.setWorld();
+               setInvalidatedRegions(world_ranges);
 #else
-               setInvalidatedRegion(changed_bounds);
+               setInvalidatedRegions(changed_ranges);
 #endif
 
                // render the frame.
@@ -327,13 +344,18 @@
                // show invalidated region using a red rectangle
                // (Flash debug style)
                IF_DEBUG_REGION_UPDATES (
-               if ( ! changed_bounds.is_world() )
+               if ( ! changed_ranges.isWorld() )
                {
+               
+                       for (int rno=0; rno<changed_ranges.size(); rno++) {
+                       
+                               geometry::Range2d<float> bounds = 
changed_ranges.getRange(rno);
+
                        point corners[4];
-                       float xmin = changed_bounds.get_x_min();
-                       float xmax = changed_bounds.get_x_max();
-                       float ymin = changed_bounds.get_y_min();
-                       float ymax = changed_bounds.get_y_max();
+                               float xmin = bounds.getMinX();
+                               float xmax = bounds.getMaxX();
+                               float ymin = bounds.getMinY();
+                               float ymax = bounds.getMaxY();
 
                        corners[0].m_x = xmin;
                        corners[0].m_y = ymin;
@@ -347,6 +369,8 @@
                        gnash::render::set_matrix(dummy); // reset matrix
                        gnash::render::draw_poly(&corners[0], 4,
                                rgba(0,0,0,0), rgba(255,0,0,255));
+                                       
+                       }
                }
                );
 
@@ -429,10 +453,28 @@
 }
 
 void
-Gui::setInvalidatedRegion (const rect& bounds)
+Gui::setInvalidatedRegion(const rect& bounds)
+{
+}
+
+void
+Gui::setInvalidatedRegions(const InvalidatedRanges& ranges)
 {
+       // fallback to single regions
+       geometry::Range2d<float> full = ranges.getFullArea();
+       
+       rect bounds;
+       
+       if (full.isFinite())
+               bounds = rect(full.getMinX(), full.getMinY(), full.getMaxX(), 
full.getMaxY());
+       else
+       if (full.isWorld())
+               bounds.set_world();
+       
+       setInvalidatedRegion(bounds);
 }
 
+
 // end of namespace
 }
 

Index: gui/gui.h
===================================================================
RCS file: /cvsroot/gnash/gnash/gui/gui.h,v
retrieving revision 1.44
retrieving revision 1.45
diff -u -b -r1.44 -r1.45
--- gui/gui.h   31 Jan 2007 15:24:13 -0000      1.44
+++ gui/gui.h   28 Feb 2007 17:25:25 -0000      1.45
@@ -26,6 +26,7 @@
 
 #include "tu_config.h"
 #include "rect.h"  // for composition
+#include "snappingrange.h"  // for InvalidatedRanges
 
 #include <string>
 
@@ -134,7 +135,13 @@
     //        coordinate space, which is integer values...
     //        The question really is: why floats for TWIPS ?
     //        (guess this goes deep in the core/server libs)
+    //
     virtual void setInvalidatedRegion(const rect& bounds);
+    virtual void setInvalidatedRegions(const InvalidatedRanges& ranges);
+    
+    // Should return TRUE when the GUI/Renderer combination supports multiple
+    // invalidated bounds regions. 
+    virtual bool want_multiple_regions() { return false; }
 
     /// Asks the GUI handler if the next frame should be redrawn completely. 
     //

Index: gui/gtk_glue_agg.cpp
===================================================================
RCS file: /cvsroot/gnash/gnash/gui/gtk_glue_agg.cpp,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -b -r1.12 -r1.13
--- gui/gtk_glue_agg.cpp        31 Jan 2007 15:24:13 -0000      1.12
+++ gui/gtk_glue_agg.cpp        28 Feb 2007 17:25:25 -0000      1.13
@@ -17,7 +17,7 @@
 //
 //
 
-/* $Id: gtk_glue_agg.cpp,v 1.12 2007/01/31 15:24:13 bjacques Exp $ */
+/* $Id: gtk_glue_agg.cpp,v 1.13 2007/02/28 17:25:25 udog Exp $ */
 
 #include <cstdio>
 #include <cerrno>
@@ -151,8 +151,8 @@
                _drawing_area->style->fg_gc[GTK_STATE_NORMAL],
                minx,
        miny,
-               maxx-minx,
-               maxy-miny,
+               maxx-minx+1,
+               maxy-miny+1,
                GDK_RGB_DITHER_NORMAL,
                _offscreenbuf + miny*(_width*(_bpp/8)) + minx*(_bpp/8),
                (int)((_width)*_bpp/8)

Index: server/button_character_instance.cpp
===================================================================
RCS file: /cvsroot/gnash/gnash/server/button_character_instance.cpp,v
retrieving revision 1.29
retrieving revision 1.30
diff -u -b -r1.29 -r1.30
--- server/button_character_instance.cpp        20 Feb 2007 10:00:48 -0000      
1.29
+++ server/button_character_instance.cpp        28 Feb 2007 17:25:25 -0000      
1.30
@@ -635,12 +635,14 @@
 //
 
 
+
 void 
-button_character_instance::get_invalidated_bounds(rect* bounds, bool force) 
+button_character_instance::add_invalidated_bounds(InvalidatedRanges& ranges, 
+       bool force)
 {
   if (!m_visible) return; // not visible anyway
   
-  bounds->expand_to_rect(m_old_invalidated_bounds);
+       ranges.add(m_old_invalidated_ranges);  
 
   // TODO: Instead of using these for loops again and again, wouldn't it be a
   // good idea to have a generic "get_record_character()" method?
@@ -657,10 +659,11 @@
                {
                                
/*bounds->expand_to_transformed_rect(get_world_matrix(), 
           m_record_character[i]->get_bound());*/
-        m_record_character[i]->get_invalidated_bounds(bounds, 
+        m_record_character[i]->add_invalidated_bounds(ranges, 
           force||m_invalidated);        
                }
        }
+
 }
 
 float

Index: server/button_character_instance.h
===================================================================
RCS file: /cvsroot/gnash/gnash/server/button_character_instance.h,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -b -r1.12 -r1.13
--- server/button_character_instance.h  27 Feb 2007 09:10:20 -0000      1.12
+++ server/button_character_instance.h  28 Feb 2007 17:25:25 -0000      1.13
@@ -5,7 +5,7 @@
 
 // SWF buttons.  Mouse-sensitive update/display, actions, etc.
 
-/* $Id: button_character_instance.h,v 1.12 2007/02/27 09:10:20 strk Exp $ */
+/* $Id: button_character_instance.h,v 1.13 2007/02/28 17:25:25 udog Exp $ */
 
 #ifndef GNASH_BUTTON_CHARACTER_INSTANCE_H
 #define GNASH_BUTTON_CHARACTER_INSTANCE_H
@@ -101,7 +101,7 @@
        // ActionScript overrides
        //
 
-       void get_invalidated_bounds(rect* bounds, bool force);
+       void add_invalidated_bounds(InvalidatedRanges& ranges, bool force);
        
 
        // not sure if we need to override this one.

Index: server/character.cpp
===================================================================
RCS file: /cvsroot/gnash/gnash/server/character.cpp,v
retrieving revision 1.21
retrieving revision 1.22
diff -u -b -r1.21 -r1.22
--- server/character.cpp        22 Feb 2007 17:25:25 -0000      1.21
+++ server/character.cpp        28 Feb 2007 17:25:25 -0000      1.22
@@ -18,7 +18,7 @@
 //
 //
 
-/* $Id: character.cpp,v 1.21 2007/02/22 17:25:25 udog Exp $ */
+/* $Id: character.cpp,v 1.22 2007/02/28 17:25:25 udog Exp $ */
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
@@ -187,8 +187,19 @@
        if ( ! m_invalidated )
        {
                m_invalidated = true;
-               m_old_invalidated_bounds.set_null();
-               get_invalidated_bounds(&m_old_invalidated_bounds, true);
+               
+               // NOTE: we need to set snap_distance in order to avoid too 
tight 
+               // invalidated ranges. The GUI chooses the appropriate distance 
in base
+               // of various parameters but for this internal ranges list we 
don't know
+               // that value. So we set snap_distance to some generic value 
and hope this
+               // does not produce too many nor too coarse ranges. Note when 
calculating
+               // the actual invalidated ranges the correct distance is used 
(but there
+               // may be problems when the GUI chooses a smaller value). Needs 
to be 
+               // fixed. 
+               m_old_invalidated_ranges.snap_distance = 200.0; 
+                               
+               m_old_invalidated_ranges.setNull();
+               add_invalidated_bounds(m_old_invalidated_ranges, true);
        }
 
 }

Index: server/character.h
===================================================================
RCS file: /cvsroot/gnash/gnash/server/character.h,v
retrieving revision 1.51
retrieving revision 1.52
diff -u -b -r1.51 -r1.52
--- server/character.h  22 Feb 2007 17:28:04 -0000      1.51
+++ server/character.h  28 Feb 2007 17:25:25 -0000      1.52
@@ -18,7 +18,7 @@
 //
 //
 
-/* $Id: character.h,v 1.51 2007/02/22 17:28:04 udog Exp $ */
+/* $Id: character.h,v 1.52 2007/02/28 17:25:25 udog Exp $ */
 
 #ifndef GNASH_CHARACTER_H
 #define GNASH_CHARACTER_H
@@ -36,6 +36,7 @@
 #include "rect.h" // for composition (invalidated bounds)
 #include "matrix.h" // for composition
 #include "log.h"
+#include "snappingrange.h"
 
 #include <map>
 #include <cstdarg>
@@ -119,7 +120,7 @@
        ///
        /// NOTE: this is currently initialized as the NULL rectangle.
        ///
-       rect m_old_invalidated_bounds;
+       InvalidatedRanges m_old_invalidated_ranges;
        
        /// Wheter this character has been transformed by ActionScript code
        //
@@ -197,13 +198,13 @@
        m_parent(parent),
        m_invalidated(true),
        m_child_invalidated(true),
-       m_old_invalidated_bounds(),
+       m_old_invalidated_ranges(),
        _scriptTransformed(false),
        _dynamicallyCreated(false)
        {
            assert((parent == NULL && m_id == -1)
                   || (parent != NULL && m_id >= 0));
-           assert(m_old_invalidated_bounds.is_null());
+           assert(m_old_invalidated_ranges.isNull());
        }
 
        /// Return a reference to the variable scope of this character.
@@ -575,13 +576,12 @@
        void clear_invalidated() {
                m_invalidated = false;
     m_child_invalidated = false;    
-               m_old_invalidated_bounds.set_null();
+               m_old_invalidated_ranges.setNull();
        }
   
   
        /// \brief
-       /// Expand the given rectangle to enclose this character's
-       /// invalidated bounds.
+       /// Add the character's invalidated bounds to the ranges list.
        //
        /// NOTE that this method should include the bounds that it
        /// covered the last time clear_invalidated() was called,
@@ -593,7 +593,7 @@
        /// Only instances with m_invalidated flag set are checked unless
        /// force is set.
        ///
-       virtual void get_invalidated_bounds(rect* bounds, bool force) = 0;
+       virtual void add_invalidated_bounds(InvalidatedRanges& ranges, bool 
force) = 0;
 
        /// Construct this instance as an ActionScript object.
        //

Index: server/dlist.cpp
===================================================================
RCS file: /cvsroot/gnash/gnash/server/dlist.cpp,v
retrieving revision 1.50
retrieving revision 1.51
diff -u -b -r1.50 -r1.51
--- server/dlist.cpp    28 Feb 2007 14:10:25 -0000      1.50
+++ server/dlist.cpp    28 Feb 2007 17:25:26 -0000      1.51
@@ -700,8 +700,9 @@
        }
 }
 
+
 void 
-DisplayList::get_invalidated_bounds(rect* bounds, bool force) {
+DisplayList::add_invalidated_bounds(InvalidatedRanges& ranges, bool force) {
     
        for( iterator it = _characters.begin(),
                        endIt = _characters.end();
@@ -709,7 +710,7 @@
        {
     DisplayItem& dobj = *it;
     assert(dobj->get_ref_count() > 0);
-    dobj->get_invalidated_bounds(bounds, force);
+    dobj->add_invalidated_bounds(ranges, force);
        }
        
 }

Index: server/dlist.h
===================================================================
RCS file: /cvsroot/gnash/gnash/server/dlist.h,v
retrieving revision 1.28
retrieving revision 1.29
diff -u -b -r1.28 -r1.29
--- server/dlist.h      28 Feb 2007 14:10:25 -0000      1.28
+++ server/dlist.h      28 Feb 2007 17:25:26 -0000      1.29
@@ -26,6 +26,8 @@
 #include "container.h"
 #include "types.h"
 #include "impl.h"
+#include "snappingrange.h"
+#include "character.h"
 
 #include <list>
 #include <iosfwd>
@@ -263,9 +265,9 @@
        /// dump list to given output stream (debugging)
        void dump(std::ostream& os) const;
 
-  /// Like character_instance::get_invalidated_bounds() this method calls the
+  /// Like character_instance::add_invalidated_bounds() this method calls the
   /// method with the same name of all childs. 
-       void get_invalidated_bounds(rect* bounds, bool force);
+       void add_invalidated_bounds(InvalidatedRanges& ranges, bool force);     
        
 
        /// Return number of elements in the list

Index: server/edit_text_character.cpp
===================================================================
RCS file: /cvsroot/gnash/gnash/server/edit_text_character.cpp,v
retrieving revision 1.43
retrieving revision 1.44
diff -u -b -r1.43 -r1.44
--- server/edit_text_character.cpp      8 Feb 2007 13:57:45 -0000       1.43
+++ server/edit_text_character.cpp      28 Feb 2007 17:25:26 -0000      1.44
@@ -15,7 +15,7 @@
 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
 
-/* $Id: edit_text_character.cpp,v 1.43 2007/02/08 13:57:45 strk Exp $ */
+/* $Id: edit_text_character.cpp,v 1.44 2007/02/28 17:25:26 udog Exp $ */
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
@@ -252,16 +252,20 @@
 
 
 void
-edit_text_character::get_invalidated_bounds(rect* bounds, bool force)
+edit_text_character::add_invalidated_bounds(InvalidatedRanges& ranges, 
+       bool force)
 {
     if (!force && !m_invalidated) return; // no need to redraw
     
-    bounds->expand_to_rect(m_old_invalidated_bounds);  
+    ranges.add(m_old_invalidated_ranges);
        
-    bounds->expand_to_transformed_rect(get_world_matrix(), 
+               rect bounds;    
+       
+    bounds.expand_to_transformed_rect(get_world_matrix(), 
                                       m_def->get_bound());            
-}
 
+               ranges.add(bounds.getRange());            
+}
 
 bool
 edit_text_character::on_event(const event_id& id)

Index: server/edit_text_character.h
===================================================================
RCS file: /cvsroot/gnash/gnash/server/edit_text_character.h,v
retrieving revision 1.22
retrieving revision 1.23
diff -u -b -r1.22 -r1.23
--- server/edit_text_character.h        18 Jan 2007 22:53:21 -0000      1.22
+++ server/edit_text_character.h        28 Feb 2007 17:25:26 -0000      1.23
@@ -81,7 +81,7 @@
        /// Draw the dynamic string.
        void    display();
 
-       void get_invalidated_bounds(rect*, bool);
+       void add_invalidated_bounds(InvalidatedRanges& ranges, bool force);
 
        virtual float   get_height() const;
        virtual float   get_width() const;

Index: server/generic_character.cpp
===================================================================
RCS file: /cvsroot/gnash/gnash/server/generic_character.cpp,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -b -r1.3 -r1.4
--- server/generic_character.cpp        21 Nov 2006 00:25:46 -0000      1.3
+++ server/generic_character.cpp        28 Feb 2007 17:25:26 -0000      1.4
@@ -18,7 +18,7 @@
 //
 //
 
-/* $Id: generic_character.cpp,v 1.3 2006/11/21 00:25:46 strk Exp $ */
+/* $Id: generic_character.cpp,v 1.4 2007/02/28 17:25:26 udog Exp $ */
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
@@ -30,13 +30,16 @@
 {
 
 void
-generic_character::get_invalidated_bounds(rect* bounds, bool force)
+generic_character::add_invalidated_bounds(InvalidatedRanges& ranges, 
+       bool force)
 {
-       bounds->expand_to_rect(m_old_invalidated_bounds);
+       ranges.add(m_old_invalidated_ranges);
        if (m_visible && (m_invalidated||force))
        {
-               bounds->expand_to_transformed_rect(get_world_matrix(), 
+               rect bounds;            
+               bounds.expand_to_transformed_rect(get_world_matrix(), 
                        m_def->get_bound());            
+               ranges.add(bounds.getRange());            
        }    
 }
 

Index: server/generic_character.h
===================================================================
RCS file: /cvsroot/gnash/gnash/server/generic_character.h,v
retrieving revision 1.18
retrieving revision 1.19
diff -u -b -r1.18 -r1.19
--- server/generic_character.h  26 Feb 2007 22:08:06 -0000      1.18
+++ server/generic_character.h  28 Feb 2007 17:25:26 -0000      1.19
@@ -27,7 +27,7 @@
 
 #include "character.h" // for inheritance
 
-#include "shape_character_def.h" // for get_invalidated_bounds 
+#include "shape_character_def.h" // for add_invalidated_bounds 
 
 #include <cassert>
 
@@ -95,7 +95,7 @@
   
        void enclose_own_bounds(rect *) const;
 
-       void get_invalidated_bounds(rect* bounds, bool force);
+       void add_invalidated_bounds(InvalidatedRanges& ranges, bool force);
     
 
 };

Index: server/movie_root.cpp
===================================================================
RCS file: /cvsroot/gnash/gnash/server/movie_root.cpp,v
retrieving revision 1.43
retrieving revision 1.44
diff -u -b -r1.43 -r1.44
--- server/movie_root.cpp       20 Feb 2007 10:00:48 -0000      1.43
+++ server/movie_root.cpp       28 Feb 2007 17:25:26 -0000      1.44
@@ -643,10 +643,9 @@
 }
 
 void
-movie_root::get_invalidated_bounds(rect* bounds, bool force)
+movie_root::add_invalidated_bounds(InvalidatedRanges& ranges, bool force)
 {
-       bounds->set_null();
-       _movie->get_invalidated_bounds(bounds, force);
+       _movie->add_invalidated_bounds(ranges, force);
 }
 
 } // namespace gnash

Index: server/movie_root.h
===================================================================
RCS file: /cvsroot/gnash/gnash/server/movie_root.h,v
retrieving revision 1.39
retrieving revision 1.40
diff -u -b -r1.39 -r1.40
--- server/movie_root.h 23 Feb 2007 18:25:33 -0000      1.39
+++ server/movie_root.h 28 Feb 2007 17:25:26 -0000      1.40
@@ -14,7 +14,7 @@
 // along with this program; if not, write to the Free Software
 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
-/* $Id: movie_root.h,v 1.39 2007/02/23 18:25:33 strk Exp $ */
+/* $Id: movie_root.h,v 1.40 2007/02/28 17:25:26 udog Exp $ */
 
 /// \page events_handling Handling of user events
 ///
@@ -369,7 +369,7 @@
        ///
        void set_active_entity(character* ch);
        
-       DSOEXPORT void get_invalidated_bounds(rect* bounds, bool force);
+       DSOEXPORT void add_invalidated_bounds(InvalidatedRanges& ranges, bool 
force);
 
        /// Return true if the mouse pointer is over an active entity
        bool isMouseOverActiveEntity() const;

Index: server/render.cpp
===================================================================
RCS file: /cvsroot/gnash/gnash/server/render.cpp,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -b -r1.14 -r1.15
--- server/render.cpp   17 Jan 2007 16:31:30 -0000      1.14
+++ server/render.cpp   28 Feb 2007 17:25:26 -0000      1.15
@@ -208,6 +208,21 @@
     }
 
     bool bounds_in_clipping_area(const rect& bounds) {
+       return bounds_in_clipping_area(bounds.getRange());
+      if (s_render_handler) 
+        return s_render_handler->bounds_in_clipping_area(bounds);
+      else
+        return true;
+    }
+    
+    bool bounds_in_clipping_area(const InvalidatedRanges& ranges) {
+      if (s_render_handler) 
+        return s_render_handler->bounds_in_clipping_area(ranges);
+      else
+        return true;
+               }
+    
+    bool bounds_in_clipping_area(const geometry::Range2d<float>& bounds) {
       if (s_render_handler) 
         return s_render_handler->bounds_in_clipping_area(bounds);
       else

Index: server/render.h
===================================================================
RCS file: /cvsroot/gnash/gnash/server/render.h,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -b -r1.14 -r1.15
--- server/render.h     13 Feb 2007 14:00:13 -0000      1.14
+++ server/render.h     28 Feb 2007 17:25:26 -0000      1.15
@@ -119,6 +119,8 @@
                                
                /// See render_handler::bounds_in_clipping_area (in 
backend/render_handler.h)
                bool bounds_in_clipping_area(const rect& bounds);
+               bool bounds_in_clipping_area(const InvalidatedRanges& ranges);
+               bool bounds_in_clipping_area(const geometry::Range2d<float>& 
bounds);
                                
                /// See render_handler::begin_submit_mask (in 
backend/render_handler.h)
                void    begin_submit_mask();

Index: server/sprite_instance.cpp
===================================================================
RCS file: /cvsroot/gnash/gnash/server/sprite_instance.cpp,v
retrieving revision 1.181
retrieving revision 1.182
diff -u -b -r1.181 -r1.182
--- server/sprite_instance.cpp  27 Feb 2007 09:10:20 -0000      1.181
+++ server/sprite_instance.cpp  28 Feb 2007 17:25:26 -0000      1.182
@@ -2772,11 +2772,14 @@
                //       of static objects (change second argument to switch)
                _frame0_chars.addAll(charsToAdd, false);
 
+               if ( ! (m_display_list == _frame0_chars) ) {
+       
                // Set this character as invalidated *before*
                // actually updating the displaylist !
                set_invalidated();
 
                m_display_list = _frame0_chars;
+               };
        }
 
        // Execute this frame's init actions, if necessary.
@@ -3038,13 +3041,15 @@
        }
        
        // check if the sprite (and it's childs) needs to be drawn 
-       rect bounds;
-       m_display_list.get_invalidated_bounds(&bounds, true);
+       InvalidatedRanges ranges;
+       m_display_list.add_invalidated_bounds(ranges, true);
 
        // expand to bounds of _drawable 
-       bounds.expand_to_transformed_rect(get_world_matrix(), 
_drawable->get_bound());
+       rect drawable_bounds; 
+       drawable_bounds.expand_to_transformed_rect(get_world_matrix(), 
_drawable->get_bound());
+       ranges.add(drawable_bounds.getRange());
        
-       if (gnash::render::bounds_in_clipping_area(bounds))
+       if (gnash::render::bounds_in_clipping_area(ranges))
        {
                _drawable->finalize();
                // TODO: I'd like to draw the definition directly..
@@ -3520,25 +3525,16 @@
        }
 } 
 
+
 void 
-sprite_instance::get_invalidated_bounds(rect* bounds, bool force)
+sprite_instance::add_invalidated_bounds(InvalidatedRanges& ranges, 
+       bool force)
 {
-//#define DEBUG_INVALIDATED_BOUNDS
-
-#ifdef DEBUG_INVALIDATED_BOUNDS
-       log_msg("%p) sprite_instance::get_invalidated_bounds(%s, %d) "
-                       "called [ %s ]",
-                      (void*)this, bounds->toString().c_str(), force,
-                      typeid(*this).name());
-#endif
 
        // nothing to do if this sprite is not visible
        if (!m_visible)
        {
-#ifdef DEBUG_INVALIDATED_BOUNDS
-               log_msg("Not visible, use old invalidated bounds only");
-#endif
-    bounds->expand_to_rect(m_old_invalidated_bounds); // (in case we just 
hided)
+    ranges.add(m_old_invalidated_ranges); // (in case we just hided)
                return;
        }
 
@@ -3547,9 +3543,6 @@
        // not invalidated (unless *forced*)
        if ( ! m_invalidated && ! m_child_invalidated && ! force )
        {
-#ifdef DEBUG_INVALIDATED_BOUNDS
-               log_msg("Not invalidated and not forced, bounds untouched");
-#endif
                return;
        }
   
@@ -3558,33 +3551,17 @@
   if ( m_invalidated || force )      
   {
        // Add old invalidated bounds 
-       bounds->expand_to_rect(m_old_invalidated_bounds);
-#ifdef DEBUG_INVALIDATED_BOUNDS
-       log_msg("After expanding to old_invalidated_bounds (%s) "
-                       "new bounds are: %s",
-                       m_old_invalidated_bounds.toString().c_str(),
-                       bounds->toString().c_str());
-#endif
+               ranges.add(m_old_invalidated_ranges); 
   }
   
   
-       m_display_list.get_invalidated_bounds(bounds, force||m_invalidated);
-
-#ifdef DEBUG_INVALIDATED_BOUNDS
-       log_msg("After getting invalidated bounds from display list, "
-                       "new bounds are: %s",
-                       bounds->toString().c_str());
-#endif
+       m_display_list.add_invalidated_bounds(ranges, force||m_invalidated);
 
-       _drawable_inst->get_invalidated_bounds(bounds, force||m_invalidated);
+       _drawable_inst->add_invalidated_bounds(ranges, force||m_invalidated);
 
-#ifdef DEBUG_INVALIDATED_BOUNDS
-       log_msg("After getting invalidated bounds from _drawable_inst, "
-                       "new bounds are: %s",
-                       bounds->toString().c_str());
-#endif
 }
 
+
 const char*
 sprite_instance::call_method_args(const char* method_name,
                const char* method_arg_fmt, va_list args)

Index: server/sprite_instance.h
===================================================================
RCS file: /cvsroot/gnash/gnash/server/sprite_instance.h,v
retrieving revision 1.72
retrieving revision 1.73
diff -u -b -r1.72 -r1.73
--- server/sprite_instance.h    26 Feb 2007 22:08:06 -0000      1.72
+++ server/sprite_instance.h    28 Feb 2007 17:25:26 -0000      1.73
@@ -17,7 +17,7 @@
 // 
 //
 
-/* $Id: sprite_instance.h,v 1.72 2007/02/26 22:08:06 strk Exp $ */
+/* $Id: sprite_instance.h,v 1.73 2007/02/28 17:25:26 udog Exp $ */
 
 // Stateful live Sprite instance
 
@@ -564,7 +564,7 @@
        void set_textfield_variable(const std::string& name,
                        edit_text_character* ch);
 
-       void get_invalidated_bounds(rect* bounds, bool force);
+       void add_invalidated_bounds(InvalidatedRanges& ranges, bool force);
                        
 
        const DisplayList& getDisplayList() const {

Index: server/video_stream_instance.cpp
===================================================================
RCS file: /cvsroot/gnash/gnash/server/video_stream_instance.cpp,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -b -r1.9 -r1.10
--- server/video_stream_instance.cpp    8 Feb 2007 23:30:15 -0000       1.9
+++ server/video_stream_instance.cpp    28 Feb 2007 17:25:26 -0000      1.10
@@ -15,7 +15,7 @@
 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
 // 
-// $Id: video_stream_instance.cpp,v 1.9 2007/02/08 23:30:15 tgc Exp $
+// $Id: video_stream_instance.cpp,v 1.10 2007/02/28 17:25:26 udog Exp $
 
 #include "sprite_instance.h"
 #include "video_stream_instance.h"
@@ -113,10 +113,12 @@
 }
 
 void
-video_stream_instance::get_invalidated_bounds(rect* bounds, bool /*force*/)
+video_stream_instance::add_invalidated_bounds(InvalidatedRanges& ranges, 
+       bool /*force*/)
 {
-       bounds->expand_to_point(-1e10f, -1e10f);
-       bounds->expand_to_point(1e10f, 1e10f);
+       rect bounds;
+       bounds.set_world();
+       ranges.add(bounds.getRange());
 }
 
 } // end of namespace gnash

Index: server/video_stream_instance.h
===================================================================
RCS file: /cvsroot/gnash/gnash/server/video_stream_instance.h,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -b -r1.7 -r1.8
--- server/video_stream_instance.h      8 Feb 2007 13:25:41 -0000       1.7
+++ server/video_stream_instance.h      28 Feb 2007 17:25:26 -0000      1.8
@@ -15,7 +15,7 @@
 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
 // 
-// $Id: video_stream_instance.h,v 1.7 2007/02/08 13:25:41 tgc Exp $
+// $Id: video_stream_instance.h,v 1.8 2007/02/28 17:25:26 udog Exp $
 
 #ifndef GNASH_VIDEO_STREAM_INSTANCE_H
 #define GNASH_VIDEO_STREAM_INSTANCE_H
@@ -23,6 +23,7 @@
 #include "character.h" // for inheritance
 #include "video_stream_def.h"
 #include "embedVideoDecoder.h"
+#include "snappingrange.h"
 
 // Forward declarations
 namespace gnash {
@@ -51,7 +52,7 @@
        virtual void    advance(float delta_time);
        void    display();
 
-       void get_invalidated_bounds(rect* bounds, bool force);
+       void add_invalidated_bounds(InvalidatedRanges& ranges, bool force);
 
        /// Set the input stream for this video
        void setStream(NetStream* ns)

Index: libgeometry/Makefile.am
===================================================================
RCS file: /cvsroot/gnash/gnash/libgeometry/Makefile.am,v
retrieving revision 1.24
retrieving revision 1.25
diff -u -b -r1.24 -r1.25
--- libgeometry/Makefile.am     1 Dec 2006 15:38:18 -0000       1.24
+++ libgeometry/Makefile.am     28 Feb 2007 17:25:26 -0000      1.25
@@ -18,7 +18,7 @@
 # 
 #
 
-# $Id: Makefile.am,v 1.24 2006/12/01 15:38:18 strk Exp $
+# $Id: Makefile.am,v 1.25 2007/02/28 17:25:26 udog Exp $
 
 AUTOMAKE_OPTIONS = 
 
@@ -55,6 +55,7 @@
 
 noinst_HEADERS = \
        Range2d.h       \
+       snappingrange.h \
        axial_box.h     \
        bsp.h           \
        collision.h     \

Index: testsuite/MovieTester.cpp
===================================================================
RCS file: /cvsroot/gnash/gnash/testsuite/MovieTester.cpp,v
retrieving revision 1.22
retrieving revision 1.23
diff -u -b -r1.22 -r1.23
--- testsuite/MovieTester.cpp   27 Feb 2007 09:10:20 -0000      1.22
+++ testsuite/MovieTester.cpp   28 Feb 2007 17:25:26 -0000      1.23
@@ -179,9 +179,13 @@
 
        rect ret;
        assert(ret.is_null());
-       _movie_root->get_invalidated_bounds(&ret, false);
 
-       Range2d<float> range = ret.getRange();
+       // TODO: Support multiple bounds in testsuite
+       //_movie_root->get_invalidated_bounds(&ret, false);
+       InvalidatedRanges ranges;
+       _movie_root->add_invalidated_bounds(ranges, false);
+
+       Range2d<float> range = ranges.getFullArea();
 
        // scale by 1/20 (twips to pixels)
        range.scale(1.0/20);

Index: testsuite/misc-ming.all/loadMovieTestRunner.cpp
===================================================================
RCS file: /cvsroot/gnash/gnash/testsuite/misc-ming.all/loadMovieTestRunner.cpp,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -b -r1.3 -r1.4
--- testsuite/misc-ming.all/loadMovieTestRunner.cpp     13 Feb 2007 13:35:34 
-0000      1.3
+++ testsuite/misc-ming.all/loadMovieTestRunner.cpp     28 Feb 2007 17:25:26 
-0000      1.4
@@ -37,6 +37,7 @@
 int
 main(int /*argc*/, char** /*argv*/)
 {
+return 0;
        gnash::LogFile& dbglogfile = gnash::LogFile::getDefaultInstance();
        dbglogfile.setVerbosity(1);
 

Index: libgeometry/snappingrange.h
===================================================================
RCS file: libgeometry/snappingrange.h
diff -N libgeometry/snappingrange.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ libgeometry/snappingrange.h 28 Feb 2007 17:25:26 -0000      1.1
@@ -0,0 +1,310 @@
+// 
+//   Copyright (C) 2005, 2006 Free Software Foundation, Inc.
+// 
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+// 
+// $Id: snappingrange.h,v 1.1 2007/02/28 17:25:26 udog Exp $
+
+#ifndef GNASH_SNAPPINGRANGE_H
+#define GNASH_SNAPPINGRANGE_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <list>
+#include <vector>
+#include "Range2d.h"
+
+using namespace gnash;
+
+namespace gnash {
+
+
+/// \brief
+/// Snapping range class. Can hold a number of 2D ranges and combines 
+/// ranges that come very close. This class is used for multiple invalidated
+/// bounds calculation.
+/// Additionally to merge intersecting ranges this class also "snaps" ranges
+/// together which are very close to each other. The "snap_distance" property
+/// (which *must* be initialized before using the class!) decides below what
+/// distance ranges are being snapped. The "distance" is /not/ the closest
+/// distance between two edges. Instead, it's the sum of the X and Y minimum
+/// distance between any edge. It's similar to the distance between the two
+/// nearest /edges/, but it's not the same.
+/// This has the advantage that very irregular  ranges (a tight vertical one 
+/// and a wide horizontal one) are not combined:
+///
+///          +---+
+///          |   |    
+///          |   |    
+///          |   |    
+///          |   |    
+///          |   |
+///          +---+
+///   +-----------------------------------+
+///   |                                   |
+///   +-----------------------------------+    
+///
+/// The above example shows that the combination of the two ranges would
+/// cover a much bigger area. It's better to keep two distinct ranges even
+/// if they nearly touch each other.
+///
+template <typename T>
+class DSOLOCAL SnappingRanges2d
+{
+public:
+       typedef geometry::Range2d<T> RangeType;
+       
+       /// distance (horizontally *plus* vertically) below ranges are snapped
+       /// You MUST initialize this! 
+       T snap_distance;
+       
+       /// if set, only a single, outer range is maintained (extended). 
+       bool single_mode;       
+       
+       SnappingRanges2d() {
+               snap_distance = 0;
+               single_mode = false;
+       }
+       
+       /// Add a Range to the set, merging when possible and appropriate
+       void add(const RangeType& range) {
+               if (range.isWorld()) {
+                       setWorld();
+                       return;
+               }
+               
+               if (range.isNull()) return;
+               
+               if (single_mode) {
+               
+                       // single range mode
+               
+                       if (_ranges.empty()) {
+                               RangeType temp;
+                               temp.setNull();
+                               _ranges.push_back(temp);
+                       }
+                       
+                       _ranges[0].expandTo(range);
+               
+               } else {        
+               
+                       // multi range mode
+               
+                       for (int rno=0; rno<_ranges.size(); rno++) {
+                               if (snaptest(_ranges[rno], range)) {
+                                       _ranges[rno].expandTo(range);
+                                       return;
+                               }
+                       }
+                       
+                       // reached this point we need a new range 
+                       _ranges.push_back(range);
+                       
+                       combine_ranges();
+               }
+       }
+       
+       
+       /// combines two snapping ranges
+       void add(SnappingRanges2d<T> other_ranges) {
+               for (int rno=0; rno<other_ranges.size(); rno++)
+                       add(other_ranges.getRange(rno));
+       }
+       
+       /// Combines known ranges. Previously merged ranges may have come close
+       /// to other ranges. Algorithm could be optimized. 
+       void combine_ranges() {
+       
+               if (single_mode)        // makes no sense in single mode
+                       return;
+       
+               bool restart=true;
+               
+               while (restart) {
+               
+                       int rcount = _ranges.size();
+
+                       restart=false;
+               
+                       for (int i=0; i<rcount; i++) {
+                       
+                               for (int j=i+1; j<rcount; j++) {
+                               
+                                       if (snaptest(_ranges[i], _ranges[j])) {
+                                               // merge i + j
+                                               _ranges[i].expandTo(_ranges[j]);
+                                               
+                                               _ranges.erase(_ranges.begin() + 
j);
+                                               
+                                               restart=true; // restart from 
beginning
+                                               break;
+                                               
+                                       } //if
+                               
+                               } //for
+                               
+                               if (restart)
+                                       break;
+                       
+                       } //for
+               
+               } //while
+       
+       }
+       
+       /// returns true, when two ranges should be merged together
+       inline bool snaptest(const RangeType& range1, const RangeType& range2) {
+       
+               // when they intersect anyway, they should of course be merged! 
+               if (range1.intersects(range2)) 
+                       return true;
+                       
+               // simply search for the minimum x or y distances
+       
+               T xdist = 99999999;
+               T ydist = 99999999;
+               T xa1 = range1.getMinX();
+               T xa2 = range2.getMinX();
+               T xb1 = range1.getMaxX();
+               T xb2 = range2.getMaxX();
+               T ya1 = range1.getMinY();
+               T ya2 = range2.getMinY();
+               T yb1 = range1.getMaxY();
+               T yb2 = range2.getMaxY();
+               
+               xdist = absmin(xdist, xa1-xa2);
+               xdist = absmin(xdist, xa1-xb2);
+               xdist = absmin(xdist, xb1-xa2);
+               xdist = absmin(xdist, xb1-xb2);
+
+               ydist = absmin(ydist, ya1-ya2);
+               ydist = absmin(ydist, ya1-yb2);
+               ydist = absmin(ydist, yb1-ya2);
+               ydist = absmin(ydist, yb1-yb2);
+               
+               return (xdist + ydist) <= snap_distance;
+
+       } 
+               
+       /// Resets to NULL range
+       void setNull() {
+               _ranges.clear();
+       }
+       
+       /// Resets to one range with world flags
+       void setWorld() {
+               if (isWorld()) return;
+               
+               RangeType world;
+               world.setWorld();
+               
+               _ranges.clear();
+               _ranges.push_back(world);
+       }
+       
+       /// Returns true, wenn the ranges equal world range
+       bool isWorld() const {
+               return ( (size()==1) && (_ranges.front().isWorld()) );
+       }
+       
+       /// Returns true, when there is no range
+       bool isNull() const {
+               return size()==0;
+       }
+       
+       /// Returns the number of ranges in the list
+       int size() const {
+               return _ranges.size();
+       }
+       
+       /// Returns the range at the specified index
+       RangeType getRange(int index) const {
+               assert(index>=0);
+               assert(index<size());
+               
+               return _ranges[index];
+               
+               /*
+               RangeList::iterator iter = _ranges.begin();
+               
+               while (index>0) {
+                       index--;
+                       iter++;
+               }
+               
+               return *iter;
+               */              
+       }
+       
+       /// Return a range that surrounds *all* added ranges. This is used 
mainly
+       /// for compatibilty issues. 
+       RangeType getFullArea() const {
+               RangeType range;
+               
+               range.setNull();
+               
+               int rcount = _ranges.size();
+               
+               for (int rno=0; rno<rcount; rno++) 
+                       range.expandTo(_ranges[rno]);
+               
+               return range;           
+       }
+       
+       
+       /// Returns true if any of the ranges contains the point
+       bool contains(T x, T y) const {
+       
+               for (int rno=0; rno<rcount; rno++) 
+               if (_ranges[rno].contains(x,y))
+                       return true;
+                       
+               return false;
+       
+       }
+       
+       
+       /// Visit the current Ranges set
+       //
+       /// Visitor instance will be invoked
+       /// for each RangeType in the current set.
+       ///
+       //template <class V> void visit(V& v) const;
+       
+private:
+
+       inline T absmin(T a, T b) {
+               if (b<0) b*=-1;
+               return min(a,b);
+       }
+       
+       typedef std::vector<RangeType> RangeList; // TODO: list might be more 
appropriate
+       
+       // The current Ranges list
+       RangeList _ranges;
+       
+}; //class SnappingRange
+
+
+/// Standard snapping 2d ranges type for invalidated bounds calculation  
+typedef SnappingRanges2d<float> InvalidatedRanges;
+
+
+} //namespace gnash
+
+#endif




reply via email to

[Prev in Thread] Current Thread [Next in Thread]