gnash-commit
[Top][All Lists]
Advanced

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

[Gnash-commit] gnash/backend render_handler_agg.cpp


From: Sandro Santilli
Subject: [Gnash-commit] gnash/backend render_handler_agg.cpp
Date: Sat, 07 Oct 2006 16:15:20 +0000

CVSROOT:        /sources/gnash
Module name:    gnash
Changes by:     Sandro Santilli <strk>  06/10/07 16:15:20

Modified files:
        backend        : render_handler_agg.cpp 

Log message:
        CR => NL

CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/gnash/backend/render_handler_agg.cpp?cvsroot=gnash&r1=1.1&r2=1.2

Patches:
Index: render_handler_agg.cpp
===================================================================
RCS file: /sources/gnash/gnash/backend/render_handler_agg.cpp,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -b -r1.1 -r1.2
--- render_handler_agg.cpp      7 Oct 2006 14:05:44 -0000       1.1
+++ render_handler_agg.cpp      7 Oct 2006 16:15:20 -0000       1.2
@@ -1 +1,1305 @@
-// 
//   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

// Linking Gnash statically or dynamically with other modules is making a
// combined work based on Gnash. Thus, the terms and conditions of the GNU
// General Public License cover the whole combination.
//
// As a special exception, the copyright holders of Gnash give you
// permission to combine Gnash with free software programs or libraries
// that are released under the GNU LGPL and with code included in any
// release of Talkback distributed by the Mozilla Foundation. You may
// copy and distribute such a system following the terms of the GNU GPL
// for all but the LGPL-covered parts and Talkback, and following the
// LGPL for the LGPL-covered parts.
//
// Note that people who make modified versions of Gnash are not obligated
// to grant this special exception for their modified versions; it is their
// choice whether to do so. The GNU General Public License gives permission
// to release a modified version without this exception; this exception
// also makes it possible to release a modified version which carries
// forward this exception.
 

// Original version by Udo Giacomozzi and Hannes Mayr, 
// INDUNET GmbH (www.indunet.it)


/// A render_handler that uses the Anti-Grain Geometry Toolkit (antigrain.com)
/// and renders directly to a buffer (for example to the framebuffer). This 
/// backend is *completely* independent of any hardware. It can be used for
/// rendering to the Linux FrameBuffer device, or be blitted inside a 
/// window (regardless of what operating system). It should also be no problem
/// to render into a file...

/*

Status:

  outlines:
    solid          COMPLETE
    patterns       NOT IMPLEMENTED (seems like Gnash does not support them yet)
    widths         COMPLETE
    colors, alpha  COMPLETE
    
  fills:
    solid fills    COMPLETE
    gradients      NOT IMPLEMENTED
    bitmaps        NOT IMPLEMENTED
    
  fonts            COMPLETE
    
  masks            NOT IMPLEMENTED (masks are drawn as shapes)
  
  caching          currently working on it...
  
  video            don't know how that works    

*/


#include "gnash.h"
#include "types.h"
#include "image.h"
#include "utility.h"
#include "log.h"
#include "render_handler.h"
//#include "render_handler_tri.h"   // test only!

#include "shape_character_def.h" 
#include "generic_character.h"  

#include "agg_rendering_buffer.h"
#include "agg_renderer_base.h"
#include "agg_pixfmt_rgb.h"
#include "agg_pixfmt_rgb_packed.h"
#include "agg_pixfmt_rgba.h"
#include "agg_pixfmt_gray.h"
#include "agg_color_rgba.h"
#include "agg_color_gray.h"
#include "agg_ellipse.h"
#include "agg_conv_transform.h"
#include "agg_trans_affine.h"
#include "agg_scanline_u.h"
#include "agg_scanline_p.h"
#include "agg_renderer_scanline.h"
#include "agg_rasterizer_scanline_aa.h"
#include "agg_rasterizer_compound_aa.h"
#include "agg_span_allocator.h"
#include "agg_path_storage.h"
#include "agg_conv_curve.h"
#include "agg_conv_stroke.h"
#include "agg_vcgen_stroke.h"
#include "agg_bezier_arc.h"
#include "agg_renderer_primitives.h"
#include "agg_gamma_functions.h"
#include "agg_math_stroke.h"



using namespace gnash;




class bitmap_info_agg : public gnash::bitmap_info
{
public:

  bitmap_info_agg() {
    //log_msg("bitmap_info_agg instance");
    // dummy
  }

};


namespace gnash {

// --- CACHE -------------------------------------------------------------------

// Possible caching mechanisms (ideas):
//  - cache paths after applying matrix
//  - cache characters as bitmap
//  - try to update only changed parts of the stage!! This may require 
//    additional code in the character instances, something like a 
//    "invalidated" flag.
//  - smart cache: start caching after 3 or 5 hyptothetical cache hits
//    (hits to cache objects that contain no data, yet)
 

/// This class holds a completely transformed path (fixed position). Speeds
/// up characters that stay fixed on a certain position on the stage. 
// ***CURRENTLY***NOT***USED***
class agg_transformed_path 
{
  /// Original transformation matrix 
  matrix m_mat;  
  
  /// Number of cache hits 
  int hits;
  
  /// Number of cache misses
  int misses;
  
  /// Contents of this cache item. First dimension is fill style 
  std::vector <std::vector <agg::path_storage> > data;
};

class agg_cache_manager : private render_cache_manager
{
};


// --- AGG HELPER CLASSES ------------------------------------------------------

// Style handler for AGG's compound rasterizer.
class agg_style_handler
{
public:
    agg_style_handler() : 
        m_transparent(0, 0, 0, 0)
    {}

    /// Called by AGG to ask if a certain style is a solid color
    bool is_solid(unsigned style) const { 
      return true;  // The backend currently only supports solid fills 
    }
    
    /// Adds a new solid fill color style
    void add(const agg::rgba8 color) {
      m_colors.push_back(color);
    }

    /// Returns the color of a certain fill style (solid)
    const agg::rgba8& color(unsigned style) const 
    {
        if (style < m_colors.size())
            return m_colors[style];

        return m_transparent;
    }

    /// Called by AGG to generate a scanline span for non-solid fills 
    void generate_span(agg::rgba8* span, int x, int y, unsigned len, unsigned 
style)
    { 
      // non-solid fills currently not supported
    }


private:
    std::vector<agg::rgba8> m_colors;
    agg::rgba8          m_transparent;
};  // class agg_style_handler




// --- RENDER HANDLER ----------------------------------------------------------
// The class is implemented using templates so that it supports any kind of
// pixel format. LUT (look up tables) are not supported, however.

template <class PixelFormat>
class render_handler_agg : public render_handler
{
private:
  typedef agg::renderer_base<PixelFormat> renderer_base;
    
  // TODO: Change these!!
        unsigned char *memaddr;
        int     memsize;
        int xres;
        int yres;
        int bpp;        // bits per pixel
        double scale;


  // initialize AGG
        void agg_init()
        {
        log_msg("agg_init()\n");

        // set rendering buffer
        m_rbuf.attach(memaddr, xres, yres, xres*(bpp/8));
        
        // clear screen
        renderer_base rbase(*m_pixf);
        rbase.clear(agg::rgba8(0, 0, 0));

        log_msg("AGG initialized.\n");

        //agg_drawtest();
  }

public:
  int              m_view_width;      // TODO: remove these??
  int              m_view_height;

  // Enable/disable antialiasing.
  bool  m_enable_antialias;

  // Output size.
  float m_display_width;
  float m_display_height;

  gnash::matrix m_current_matrix;
  gnash::cxform m_current_cxform;

  void set_antialiased(bool enable) {
                // dummy
  }

  // Style state.
  enum style_index
  {
        LEFT_STYLE = 0,
        RIGHT_STYLE,
        LINE_STYLE,
        STYLE_COUNT
  };


  gnash::bitmap_info*   create_bitmap_info_rgb(image::rgb* im)
        // Given an image, returns a pointer to a bitmap_info class
        // that can later be passed to fill_styleX_bitmap(), to set a
        // bitmap fill style.
        {          
          UNUSED(im);
          // bitmaps currently not supported! - return dummy for fontlib
          return new bitmap_info_agg(); 
    return NULL;
        }


  gnash::bitmap_info*   create_bitmap_info_rgba(image::rgba* im)
        // Given an image, returns a pointer to a bitmap_info class
        // that can later be passed to fill_style_bitmap(), to set a
        // bitmap fill style.
        //
        // This version takes an image with an alpha channel.
        {
          UNUSED(im);
          // bitmaps currently not supported! - return dummy for fontlib
          return new bitmap_info_agg();
    return NULL;
        }


  gnash::bitmap_info*   create_bitmap_info_empty()
        // Create a placeholder bitmap_info.  Used when
        // DO_NOT_LOAD_BITMAPS is set; then later on the host program
        // can use movie_definition::get_bitmap_info_count() and
        // movie_definition::get_bitmap_info() to stuff precomputed
        // textures into these bitmap infos.
        {
          // bitmaps currently not supported! - return dummy for fontlib
          return new bitmap_info_agg();
    return NULL;
        }

  gnash::bitmap_info*   create_bitmap_info_alpha(int w, int h, uint8_t* data)
        // Create a bitmap_info so that it contains an alpha texture
        // with the given data (1 byte per texel).
        {
          UNUSED(w);
    UNUSED(h);
    UNUSED(data); 
          // bitmaps currently not supported! - return dummy for fontlib
          return new bitmap_info_agg();
    return NULL;
        }


  void  delete_bitmap_info(gnash::bitmap_info* bi)
        // Delete the given bitmap info class.
        {
    free(bi);
        }


  // Constructor
  render_handler_agg(unsigned char *mem, int size, int x, int y,
        int bits_per_pixel)
  {
        memaddr = mem;
        memsize = size;
        xres            = x;
        yres            = y;
        bpp                     = bits_per_pixel;
        scale           = 1/20.0;

    // allocate pixel format accessor   
    m_pixf = new PixelFormat(m_rbuf);
    //m_rbase = new renderer_base(*m_pixf);  --> does not work!!??

        agg_init();
  }

  // Destructor
  ~render_handler_agg()
  {

  }

  void begin_display(
        gnash::rgba background_color,
        int viewport_x0, int viewport_y0,
        int viewport_width, int viewport_height,
        float x0, float x1, float y0, float y1)
        // Set up to render a full frame from a movie and fills the
        // background.  Sets up necessary transforms, to scale the
        // movie to fit within the given dimensions.  Call
        // end_display() when you're done.
        //
        // The rectangle (viewport_x0, viewport_y0, viewport_x0 +
        // viewport_width, viewport_y0 + viewport_height) defines the
        // window coordinates taken up by the movie.
        //
        // The rectangle (x0, y0, x1, y1) defines the pixel
        // coordinates of the movie that correspond to the viewport
        // bounds.
        {
          UNUSED(viewport_x0);
          UNUSED(viewport_y0);
          UNUSED(viewport_width);
          UNUSED(viewport_height);
          UNUSED(x0);
          UNUSED(y0);
          UNUSED(x1);
          UNUSED(y1);
          
          double scaleX, scaleY;
          
          // clear the stage using the background color   
          renderer_base rbase(*m_pixf);
    rbase.clear(agg::rgba8(background_color.m_r, background_color.m_g,
        background_color.m_b, background_color.m_a));

    // calculate final pixel scale
    scaleX = (double)xres / (double)viewport_width / 20.0;  // 20=TWIPS
    scaleY = (double)yres / (double)viewport_height / 20.0;
    scale = scaleX<scaleY ? scaleX : scaleY;
        }

  bool allow_glyph_textures() {
    // We want to render all glyphs in place 
    return false; 
  }

  void  end_display()
        // Clean up after rendering a frame.  Client program is still
        // responsible for calling glSwapBuffers() or whatever.
        {
    // nothing to do
        }

  void  set_matrix(const gnash::matrix& m)
        // Set the current transform for mesh & line-strip rendering.
        {
    // used only for drawing line strips...        
          m_current_matrix = m;
        }

  void  set_cxform(const gnash::cxform& cx)
        // Set the current color transform for mesh & line-strip rendering.
        {
    // used only for drawing line strips...        
    m_current_cxform = cx;
        }

  static void   apply_matrix(const gnash::matrix& m)
        // add user space transformation
        {
    // TODO: what's the use for this, anyway?? 
    log_msg("apply_matrix(); called - NOT IMPLEMENTED");
        }

  static void   apply_color(const gnash::rgba& c)
        // Set the given color.
        {
    // TODO: what's the use for this, anyway?? 
    log_msg("apply_color(); called - NOT IMPLEMENTED");
        }


  void  line_style_width(float width)
        {
                line_width=width;
        }



  void  draw_line_strip(const void* coords, int vertex_count, const rgba color)
        // Draw the line strip formed by the sequence of points.
        {
    point pnt;
    
    renderer_base rbase(*m_pixf);

        agg::scanline_p8 sl;
        agg::rasterizer_scanline_aa<> ras;
        agg::renderer_scanline_aa_solid<
        agg::renderer_base<PixelFormat> > ren_sl(rbase);

    agg::path_storage path;
    agg::conv_stroke<agg::path_storage> stroke(path);
    stroke.width(1);
    stroke.line_cap(agg::round_cap);
    stroke.line_join(agg::round_join);
    path.remove_all(); // Not obligatory in this case

    const int16_t *vertex = (int16_t*)coords;
    
    m_current_matrix.transform(&pnt, point(vertex[0], vertex[1]));
        path.move_to(pnt.m_x * scale, pnt.m_y * scale);

    for (vertex += 2;  vertex_count > 1;  vertex_count--, vertex += 2) {
      m_current_matrix.transform(&pnt, point(vertex[0], vertex[1]));
        path.line_to(pnt.m_x * scale, pnt.m_y * scale);
    }
                // The vectorial pipeline
        ras.add_path(stroke);

        // Set the color and render the scanlines
        ren_sl.color(agg::rgba8(color.m_r, color.m_g, color.m_b, color.m_a));
        agg::render_scanlines(ras, sl, ren_sl);

        } // draw_line_strip


  void  draw_bitmap(
        const gnash::matrix& m,
        const gnash::bitmap_info* bi,
        const gnash::rect& coords,
        const gnash::rect& uv_coords,
        gnash::rgba color)
        // Draw a rectangle textured with the given bitmap, with the
        // given color.  Apply given transform; ignore any currently
        // set transforms.
        //
        // Intended for textured glyph rendering.
        {
          UNUSED(color);
    log_msg("  draw_bitmap NOT IMPLEMENTED\n");
        }

  void begin_submit_mask()
        {
    // not implemented
        }

  void end_submit_mask()
        {
    // not implemented
        }

  void disable_mask()
        {
    // not implemented
        }
        


  /*
   Takes a list of paths and combines them according to their fill style so 
   that each path is a closed polygon. Each path will have exactly one fill 
   style.
   This is necessary because Flash uses up to two fill styles for each path,
   one for each side. It can happen that a path contains only one edge. In that
   case it just divides a shape in two (two colors). Since this is impossible
   to draw directly we need to restore the original polygons so that they can
   be drawn independently.
   
   IMPORTANT NOTE: This is currently *not* used for AGG anymore, because AGG
   has a direct rasterizer for double styles...   
   */   
  void combine_paths(std::vector<path> &paths_in, std::vector<path> &paths_out) 
{
    
    int ino;      // index of input path
    int incount;  // cache value for paths_in.size()
    int ono;      // index of output path
    int eno;      // edge index 
    
    #define EQUAL(a,b) (fabs(a-b)<0.000000001)
    
    incount = paths_in.size();
    
    /*
    Strategy: For fill style 0, each path is compared with other paths sharing
    the same fill style and it is tried to attach the new path to a already 
    known path. The start point of the new path must match the end point of the
    previous path. Only distant shapes will not find a matching path and thus
    will create a new one.
    For fill style 1 the same is done except that the path is reversed first.
    The resulting paths will use exactly one fill style (fill style 0).
    Each source path may be used for two different resulting shapes (one time
    normally and the other reversed).    
    */
    
    // browse through all paths...
    for (ino=0; ino<incount; ino++) {
    
      path &new_path = paths_in[ino];
      int found;

      // === FILL STYLE 0 ===
      
      if (new_path.m_fill0) {
        found=0;
        
        // Search paths sharing the same fill style and whose end point matches
        // the START point of <new_path>
        for (ono=0; ono < paths_out.size(); ono++) {
        
          path &cp = paths_out[ono];
          edge &last_edge = cp.m_edges.back();
          
          log_msg("  = [FS0] compare style %d with %d: ", new_path.m_fill0,
            paths_out[ono].m_fill0);
          if (new_path.m_fill0 != paths_out[ono].m_fill0) {
            log_msg("no match\n"); 
            continue;  // fill style mismatch
          }
          log_msg("match!\n");
            
          log_msg("  = [FS0] compare edge %f/%f with %f/%f: ", 
            new_path.m_ax, new_path.m_ay,
            last_edge.m_ax, last_edge.m_ay);  
          if (!EQUAL(new_path.m_ax, last_edge.m_ax) ||
              !EQUAL(new_path.m_ay, last_edge.m_ay)) {
            log_msg("no match\n");
            continue;  // cannot attach
          }
          log_msg("match!\n");
            
            
          // ==> ok, attach "this_path" to "cp"
          
          log_msg(" == [FS0] attach path #%d\n", ino);
           
          // TODO: Resize vector and set elements directly, avoiding multiple
          // resizing...
          //cp->resize( cp->size() + new_path->size() );
          
          for (eno=0; eno<new_path.m_edges.size(); eno++) {
            cp.m_edges.push_back(new_path.m_edges[eno]);
          }
          
          found=1;
          
          break;
        
        }  // for ono
        
        if (!found) {
          // We found no matching path, so start a new one...
          log_msg(" == [FS0] start new path #%d\n", ino);
          path temp = new_path;
          temp.m_fill1=0;   // use only fill style 0
          paths_out.push_back(temp);
        }

      } // if m_fill0
      
    
      // === FILL STYLE 1 ===
      
      if (new_path.m_fill1) {
        float last_cx;
        float last_cy;
        float next_cx;
        float next_cy;
        found=0;
        
        // create a new, reversed path
        path rev_path;
        rev_path.m_fill0 = new_path.m_fill1;
        rev_path.m_ax = new_path.m_edges.back().m_ax;
        rev_path.m_ay = new_path.m_edges.back().m_ay;
        last_cx = new_path.m_edges.back().m_cx;
        last_cy = new_path.m_edges.back().m_cy;
        for (eno=new_path.m_edges.size()-2; eno>=0; eno--) {  
          edge temp = new_path.m_edges[eno];
          next_cx = temp.m_cx;
          next_cy = temp.m_cy;
          temp.m_cx = last_cx;
          temp.m_cy = last_cy;
          rev_path.m_edges.push_back(temp);
          last_cx=next_cx;
          last_cy=next_cy;
        }
        
        {
          edge temp;
          temp.m_ax = new_path.m_ax;
          temp.m_ay = new_path.m_ay;
          temp.m_cx = last_cx;
          temp.m_cy = last_cy;
          rev_path.m_edges.push_back(temp);   // add anchor of new_path as last 
edge
        }
        
        
        
        // ==> now proceed just as like with fill style 0...
        
        
        
        // Search paths sharing the same fill style and whose end point matches
        // the START point of <new_path>
        for (ono=0; ono < paths_out.size(); ono++) {
        
          path &cp = paths_out[ono];
          edge &last_edge = cp.m_edges.back();
          
          log_msg("  = [FS1] compare style %d with %d: ", rev_path.m_fill0,
            paths_out[ono].m_fill0);
          if (rev_path.m_fill0 != paths_out[ono].m_fill0) {
            log_msg("no match\n"); 
            continue;  // fill style mismatch
          }
          log_msg("match!\n");
            
          log_msg("  = [FS1] compare edge %f/%f with %f/%f: ", 
            rev_path.m_ax, rev_path.m_ay,
            last_edge.m_ax, last_edge.m_ay);  
          if (!EQUAL(rev_path.m_ax, last_edge.m_ax) ||
              !EQUAL(rev_path.m_ay, last_edge.m_ay)) {
            log_msg("no match\n");
            continue;  // cannot attach
          }
          log_msg("match!\n");
            
            
          // ==> ok, attach "this_path" to "cp"
          
          log_msg(" == [FS1] attach path #%d\n", ino);
           
          // TODO: Resize vector and set elements directly, avoiding multiple
          // resizing...
          //cp->resize( cp->size() + new_path->size() );
          
          for (eno=0; eno<rev_path.m_edges.size(); eno++) {
            cp.m_edges.push_back(rev_path.m_edges[eno]);
          }
          
          found=1;
          
          break;
        
        }  // for ono
        
        if (!found) {
          // We found no matching path, so start a new one...
          log_msg(" == [FS1] start new path #%d\n", ino);
          path temp = rev_path;
          temp.m_fill1=0;   // use only fill style 0
          paths_out.push_back(temp);
        }

      }

    
    }
    
    #undef EQUAL   
    
  }



  void draw_glyph(shape_character_def *def,
      const matrix& mat, rgba color, float pixel_scale) {
      
    // create a new path with the matrix applied   
    std::vector<path> paths;    
    apply_matrix_to_path(def->get_paths(), paths, mat);
      
    // make sure m_single_fill_styles contains the required color 
    need_single_fill_style(color);

    // draw the shape
    draw_shape(paths, m_single_fill_styles, m_neutral_cxform, false);
    
    // NOTE: Do not use even-odd filling rule for glyphs!
  }


  void draw_shape_character(shape_character_def *def, 
    const matrix& mat,
    const cxform& cx,
    float pixel_scale,
    const std::vector<fill_style>& fill_styles,
    const std::vector<line_style>& line_styles) {
    
    std::vector<path> paths;
    
    apply_matrix_to_path(def->get_paths(), paths, mat);
    
    draw_shape(paths, fill_styles, cx, true);
    
    draw_outlines(paths, line_styles, cx);
  }


  /// Takes a path and translates it using the given matrix. The new path
  /// is stored in paths_out.  
  void apply_matrix_to_path(const std::vector<path> &paths_in, 
    std::vector<path> &paths_out, const matrix &mat) {
    
    int pcount, ecount;
    int pno, eno;
    
    // copy path
    paths_out = paths_in;    
    pcount = paths_out.size();
        
    
    for (pno=0; pno<pcount; pno++) {
    
      path &the_path = paths_out[pno];     
      point oldpnt(the_path.m_ax, the_path.m_ay);
      point newpnt;
       
      mat.transform(&newpnt, oldpnt);
      the_path.m_ax = newpnt.m_x;    
      the_path.m_ay = newpnt.m_y;
      
      ecount = the_path.m_edges.size();
      for (eno=0; eno<ecount; eno++) {
      
        edge &the_edge = the_path.m_edges[eno];
        
        oldpnt.m_x = the_edge.m_ax;
        oldpnt.m_y = the_edge.m_ay;
        mat.transform(&newpnt, oldpnt);
        the_edge.m_ax = newpnt.m_x;
        the_edge.m_ay = newpnt.m_y;
        
        oldpnt.m_x = the_edge.m_cx;
        oldpnt.m_y = the_edge.m_cy;
        mat.transform(&newpnt, oldpnt);
        the_edge.m_cx = newpnt.m_x;
        the_edge.m_cy = newpnt.m_y;
      
      }          
      
    } 
    
  } // apply_matrix


  /// 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).
  void draw_shape(const std::vector<path> &paths,
    const std::vector<fill_style> &fill_styles, const cxform& cx, int even_odd) 
{
    
    /*
    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...
    */
    

    // Gnash stuff 
    int pno, eno, fno;
    int pcount, ecount, fcount;
    
    // AGG stuff
    renderer_base rbase(*m_pixf);
    agg::scanline_u8 sl;                // scanline renderer
    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
    
    // debug
    int edge_count=0;
    
    // activate even-odd filling rule
    if (even_odd)
      rasc.filling_rule(agg::fill_even_odd);
    else
      rasc.filling_rule(agg::fill_non_zero);
  
      
    // tell AGG what styles are used
    fcount = fill_styles.size();
    //log_msg("%d fill styles\n", fcount);
    for (fno=0; fno<fcount; fno++) {
      rgba color = cx.transform(fill_styles[fno].get_color());
      
      // add the color to our self-made style handler (basically just a list)
      sh.add(agg::rgba8(color.m_r, color.m_g, color.m_b, color.m_a)); 
    }
    
      
    // push paths to AGG
    pcount = paths.size();
    //log_msg("%d paths\n", pcount);
    for (pno=0; pno<pcount; pno++) {
    
      const path &this_path = paths[pno];
      agg::path_storage path;
      agg::conv_curve< agg::path_storage > curve(path);
    
      // 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*scale, this_path.m_ay*scale);      
      
      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*scale, this_edge.m_ay*scale);
        else
          path.curve3(this_edge.m_cx*scale, this_edge.m_cy*scale,
                      this_edge.m_ax*scale, this_edge.m_ay*scale);
        
      }
      
      // add path to the compound rasterizer
      rasc.add_path(curve); 
    
    }
    //log_msg("%d edges\n", edge_count);
    
    // render!
    agg::render_scanlines_compound_layered(rasc, sl, rbase, alloc, sh);
    
  } // draw_shape



  /// Just like draw_shapes() except that it draws an outline.
  void draw_outlines(const std::vector<path> &paths,
    const std::vector<line_style> &line_styles, const cxform& cx) {
    
    // 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;
    
    // AGG stuff
    renderer_base rbase(*m_pixf);
    agg::scanline_p8 sl;                // scanline renderer
    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
    
    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
    
    
    pcount = paths.size();   
    for (pno=0; pno<pcount; pno++) {
      
      const path &this_path = paths[pno];
      
      if (!this_path.m_line)  
        continue;     // invisible line
        
      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*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*scale, this_path.m_ay*scale);
        
      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*scale, this_edge.m_ay*scale);
        else
          agg_path.curve3(this_edge.m_cx*scale, this_edge.m_cy*scale,
                      this_edge.m_ax*scale, this_edge.m_ay*scale);
        
      } // for edges
      
      
      ras.add_path(stroke);
      ren_sl.color(agg::rgba8(color.m_r, color.m_g, color.m_b, color.m_a));
      
      agg::render_scanlines(ras, sl, ren_sl);
    
    
    }
      
  } // draw_outlines


  
  /// Draws the given polygon.
  void  draw_poly(const point* corners, int corner_count, const rgba fill, 
    const rgba outline) {
    
    if (corner_count<1) return;
    
    // TODO: Use aliased scanline renderer instead of anti-aliased one since
    // it is undesired anyway.
    renderer_base rbase(*m_pixf);
    agg::scanline_p8 sl;
    agg::rasterizer_scanline_aa<> ras;
    agg::renderer_scanline_aa_solid<
      agg::renderer_base<PixelFormat> > ren_sl(rbase);
      
    agg::path_storage path;
    point pnt, origin;
    
    // Note: The coordinates are rounded and 0.5 is added to snap them to the 
    // center of the pixel. This avoids blurring caused by anti-aliasing.
    
    m_current_matrix.transform(&origin, 
      point(trunc(corners[0].m_x), trunc(corners[0].m_y)));
    path.move_to(trunc(origin.m_x*scale)+0.5, trunc(origin.m_y*scale)+0.5);
    
    for (int i=1; i<corner_count; i++) {
    
      m_current_matrix.transform(&pnt, point(corners[i].m_x, corners[i].m_y));
        
      path.line_to(trunc(pnt.m_x*scale)+0.5, trunc(pnt.m_y*scale)+0.5);
    }
    
    // close polygon
    path.line_to(trunc(origin.m_x*scale)+0.5, trunc(origin.m_y*scale)+0.5);
    
    // commit path
    ras.add_path(path);
    
    // fill polygon
    if (fill.m_a>0) {
      ren_sl.color(agg::rgba8(fill.m_r, fill.m_g, fill.m_b, fill.m_a));
      agg::render_scanlines(ras, sl, ren_sl);
    }
    
    // draw outline
    if (outline.m_a>0) {
      agg::conv_stroke<agg::path_storage> stroke(path);
      
      stroke.width(1);
      
      ren_sl.color(agg::rgba8(outline.m_r, outline.m_g, outline.m_b, 
outline.m_a));
      
      ras.add_path(stroke);
      agg::render_scanlines(ras, sl, ren_sl);
    }
    
  }
                      
                      
  /*
  This method is *not* being used anymore, because we use a special AGG 
  rasterizer that can deal with Flash edges directly. 
  It is kept here since it was hard work and who knows it it becomes useful
  again some day. 
  It works for 95% of the shapes. There is just a special case where it does
  not draw a curve when it should do so. I guess there is some bug in the
  combine_paths() method when reversing a path. Probably control point of the
  first or last edge of a path is wrong (gets lost) somehow.  
  */
  void draw_shape_character_old(shape_character_def *def, 
    character *inst) {
    
    // replaces: shape_character_def::display()   (both versions)
    // replaces: shape_character_def::tesselate() 

    // Gnash stuff
    std::vector<path> paths, orig_paths;
    std::vector<fill_style> fill_styles;
    path current_path;
    int paths_count;
    int pathno, edgeno, edge_count;    
    rgba color;
    int fillno;
    int fillidx;
    
    // AGG stuff
    PixelFormat pixf(rbuf);
    renderer_base rbase(pixf);
    agg::scanline_p8 sl;
    agg::rasterizer_scanline_aa<> ras;
    agg::renderer_scanline_aa_solid<
      agg::renderer_base<PixelFormat> > ren_sl(rbase);
     
    // We need one AGG path (polygon) for each fill style
    std::vector< agg::path_storage > agg_paths; 

    log_msg("draw_shape_character() called.\n");
    
    /*
    
    Flash will tend to save outlines in clockwise rotating order.
    We use the "non zero" filling rule of AGG, that is, when you raw a 
rectangle 
    in clockwise order and another rectangle inside the other with 
    counter-clockwise order, it will keep the inner rectangle transparent.
    
    AGG could also do the "even odd" filling rule where the order does not 
    matter (so we don't need to reverse fill style #1), however this leads to
    noticeable borders between polygons. Don't know exactly why, however the
    typical fill style text (Flash SDK) shows two green triangles instead of
    a green rectangle. 
    
    So, we simple draw all paths for fill style 1 in reverse order, as opposed
    to fill style 0, which is drawn normally. 
    
    */
    ras.filling_rule(agg::fill_non_zero);
    //ras.filling_rule(agg::fill_even_odd);
    
    
    //--
    
    // Combine paths, so that they are easier to draw
    orig_paths = def->get_paths();
    combine_paths(orig_paths, paths);
      
  
    
    fill_styles = def->get_fill_styles();
    
    paths_count = paths.size();     // fasten access
    
    log_msg("Fill styles vector contains %d items.\n", fill_styles.size());
    
    log_msg("Preparing paths vector...\n");
    agg_paths.resize(fill_styles.size());
    
    log_msg("Paths vector contains %d items.\n", paths_count);
    
    for (pathno=0; pathno<paths_count; pathno++) {
    
      log_msg("Processing path #%d\n", pathno);
      
      current_path = paths[pathno];
      
      log_msg("  path fill0 has index %d\n", current_path.m_fill0);
      log_msg("  path fill1 has index %d\n", current_path.m_fill1);
      
      log_msg("  path anchor at %f / %f\n", 
        current_path.m_ax/20, current_path.m_ay/20);

      edge_count = current_path.m_edges.size();      
      log_msg("  path has %d edges.\n", edge_count);
      

      //=== DRAW FILL STYLE 0 IN NORMAL ORDER ===

      fillidx = current_path.m_fill0-1;
      
      if (fillidx>=0) {
      
        agg::path_storage *the_path = &agg_paths[fillidx];
        
        log_msg("  adding path for fill style 0 (normal)\n");

        the_path->move_to(current_path.m_ax*scale, current_path.m_ay*scale);    
  
        
        for (edgeno=0; edgeno<edge_count; edgeno++) {
          log_msg("    edge #%d anchor %f/%f control %f/%f\n", edgeno,
            current_path.m_edges[edgeno].m_ax/20,
            current_path.m_edges[edgeno].m_ay/20,
            current_path.m_edges[edgeno].m_cx/20,
            current_path.m_edges[edgeno].m_cy/20);
            
          if (current_path.m_edges[edgeno].is_straight()) {
            the_path->line_to(current_path.m_edges[edgeno].m_ax*scale, 
              current_path.m_edges[edgeno].m_ay*scale);
          }
          else 
          {
            log_msg("    drawing curve\n");
            the_path->curve3(
              current_path.m_edges[edgeno].m_cx*scale, 
              current_path.m_edges[edgeno].m_cy*scale,
              current_path.m_edges[edgeno].m_ax*scale, 
              current_path.m_edges[edgeno].m_ay*scale);
          }
        } // for edge
        
        
      } // if fillidx



      //=== DRAW FILL STYLE 1 IN REVERSED ORDER ===

      fillidx = current_path.m_fill1-1;
      
      if (fillidx>=0) {
      
        float next_ax, next_ay, next_cx, next_cy;
        float last_ax, last_ay, last_cx, last_cy;
      
        agg::path_storage *the_path = &agg_paths[fillidx];
        
        log_msg("  adding path for fill style 1 (reversed)\n");

        /*the_path->move_to(current_path.m_edges[edge_count-1].m_ax*scale, 
                         current_path.m_edges[edge_count-1].m_ay*scale);*/
                         
        last_ax = current_path.m_ax*scale;       
        last_ay = current_path.m_ay*scale;
        last_cx = last_ax;        
        last_cy = last_ay;
        the_path->move_to(last_ax, last_ay);      
        
        for (edgeno=edge_count-1; edgeno>=0; edgeno--) {
          log_msg("    edge #%d anchor %f/%f control %f/%f\n", edgeno,
            current_path.m_edges[edgeno].m_ax/20,
            current_path.m_edges[edgeno].m_ay/20,
            current_path.m_edges[edgeno].m_cx/20,
            current_path.m_edges[edgeno].m_cy/20);
            
          next_ax = current_path.m_edges[edgeno].m_ax*scale;
          next_ay = current_path.m_edges[edgeno].m_ay*scale;
          next_cx = current_path.m_edges[edgeno].m_cx*scale;
          next_cy = current_path.m_edges[edgeno].m_cy*scale;
          
          log_msg("      the_path->curve3(%f, %f, %f, %f);\n", last_cx, 
last_cy, next_ax, next_ay);
          the_path->curve3(last_cx, last_cy, next_ax, next_ay);
          
          // TODO: Do not add a curve when it is a straight line
          
          last_cx = next_cx;
          last_cy = next_cy;
          
        } // for edge
        
        the_path->curve3(last_cx, last_cy, current_path.m_ax*scale, 
current_path.m_ay*scale);      
        // TODO: Do not add a curve when it is a straight line

      }
                              
    } // for path
    
    
    /*
    Ok, now we have prepared all the paths for all fill styles. Note that AGG
    won't render curves unless we use conv_curve. The next step is to feed the
    single paths to AGG with the right color.
    Some paths may be empty (unused fill styles).    
    */
    
    for (fillidx=0; fillidx<fill_styles.size(); fillidx++) {
    
      agg::conv_curve< agg::path_storage > curve(agg_paths[fillidx]);
    
      log_msg("  drawing fill style #%d ...\n", fillidx);     
    
      color = fill_styles[fillidx].get_color();
      
      log_msg("    color is R/G/B/A %d/%d/%d/%d\n", 
        color.m_r, color.m_g, color.m_b, color.m_a);
      
      ren_sl.color(agg::rgba8(color.m_r, color.m_g, color.m_b, color.m_a));    
          
      ras.add_path(curve);
    
      agg::render_scanlines(ras, sl, ren_sl);
      usleep(1000000);
     }
     
      
  }     // draw_shape_character_old

  
private:  // private methods  

  /// Returns the cache manager instance of the given character definition.
  /// Allocates a new manager if necessary.
  agg_cache_manager* get_cache_of(character* def) {
  
    if (def->m_render_cache == NULL) {
      def->m_render_cache = new agg_cache_manager;
    }
    
    return def->m_render_cache;
  
  }
  
private:  // private variables

  agg::rendering_buffer m_rbuf;  
  PixelFormat *m_pixf;
  
};      // end class render_handler_agg




// TODO: Replace "pixelformat" with a enum!

DSOEXPORT render_handler*       create_render_handler_agg(char *pixelformat, 
  unsigned char *mem, int memsize, int xres, int yres, int bpp)
{

  log_msg("framebuffer pixel format is %s", pixelformat);

  if (!strcmp(pixelformat, "RGB555"))
          return new render_handler_agg<agg::pixfmt_rgb555> (mem, memsize, 
xres, yres, bpp);
        
        else if (!strcmp(pixelformat, "RGB565"))
          return new render_handler_agg<agg::pixfmt_rgb565> (mem, memsize, 
xres, yres, bpp);
        
        else if (!strcmp(pixelformat, "RGB24"))
          return new render_handler_agg<agg::pixfmt_rgb24> (mem, memsize, xres, 
yres, bpp);
                
        else if (!strcmp(pixelformat, "BGR24"))
          return new render_handler_agg<agg::pixfmt_rgb565> (mem, memsize, 
xres, yres, bpp);
          
        else assert(0);
        
        return NULL; // avoid compiler warning
}

} // end of namespace gnash


// Local Variables:
// mode: C++
// indent-tabs-mode: t
// End:
/* vim: set cindent tabstop=8 softtabstop=4 shiftwidth=4: */
\ No newline at end of file
+// 
+//   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
+
+// Linking Gnash statically or dynamically with other modules is making a
+// combined work based on Gnash. Thus, the terms and conditions of the GNU
+// General Public License cover the whole combination.
+//
+// As a special exception, the copyright holders of Gnash give you
+// permission to combine Gnash with free software programs or libraries
+// that are released under the GNU LGPL and with code included in any
+// release of Talkback distributed by the Mozilla Foundation. You may
+// copy and distribute such a system following the terms of the GNU GPL
+// for all but the LGPL-covered parts and Talkback, and following the
+// LGPL for the LGPL-covered parts.
+//
+// Note that people who make modified versions of Gnash are not obligated
+// to grant this special exception for their modified versions; it is their
+// choice whether to do so. The GNU General Public License gives permission
+// to release a modified version without this exception; this exception
+// also makes it possible to release a modified version which carries
+// forward this exception.
+ 
+
+// Original version by Udo Giacomozzi and Hannes Mayr, 
+// INDUNET GmbH (www.indunet.it)
+
+
+/// A render_handler that uses the Anti-Grain Geometry Toolkit (antigrain.com)
+/// and renders directly to a buffer (for example to the framebuffer). This 
+/// backend is *completely* independent of any hardware. It can be used for
+/// rendering to the Linux FrameBuffer device, or be blitted inside a 
+/// window (regardless of what operating system). It should also be no problem
+/// to render into a file...
+
+/*
+
+Status:
+
+  outlines:
+    solid          COMPLETE
+    patterns       NOT IMPLEMENTED (seems like Gnash does not support them yet)
+    widths         COMPLETE
+    colors, alpha  COMPLETE
+    
+  fills:
+    solid fills    COMPLETE
+    gradients      NOT IMPLEMENTED
+    bitmaps        NOT IMPLEMENTED
+    
+  fonts            COMPLETE
+    
+  masks            NOT IMPLEMENTED (masks are drawn as shapes)
+  
+  caching          currently working on it...
+  
+  video            don't know how that works    
+
+*/
+
+
+#include "gnash.h"
+#include "types.h"
+#include "image.h"
+#include "utility.h"
+#include "log.h"
+#include "render_handler.h"
+//#include "render_handler_tri.h"   // test only!
+
+#include "shape_character_def.h" 
+#include "generic_character.h"  
+
+#include "agg_rendering_buffer.h"
+#include "agg_renderer_base.h"
+#include "agg_pixfmt_rgb.h"
+#include "agg_pixfmt_rgb_packed.h"
+#include "agg_pixfmt_rgba.h"
+#include "agg_pixfmt_gray.h"
+#include "agg_color_rgba.h"
+#include "agg_color_gray.h"
+#include "agg_ellipse.h"
+#include "agg_conv_transform.h"
+#include "agg_trans_affine.h"
+#include "agg_scanline_u.h"
+#include "agg_scanline_p.h"
+#include "agg_renderer_scanline.h"
+#include "agg_rasterizer_scanline_aa.h"
+#include "agg_rasterizer_compound_aa.h"
+#include "agg_span_allocator.h"
+#include "agg_path_storage.h"
+#include "agg_conv_curve.h"
+#include "agg_conv_stroke.h"
+#include "agg_vcgen_stroke.h"
+#include "agg_bezier_arc.h"
+#include "agg_renderer_primitives.h"
+#include "agg_gamma_functions.h"
+#include "agg_math_stroke.h"
+
+
+
+using namespace gnash;
+
+
+
+
+class bitmap_info_agg : public gnash::bitmap_info
+{
+public:
+
+  bitmap_info_agg() {
+    //log_msg("bitmap_info_agg instance");
+    // dummy
+  }
+
+};
+
+
+namespace gnash {
+
+// --- CACHE 
-------------------------------------------------------------------
+
+// Possible caching mechanisms (ideas):
+//  - cache paths after applying matrix
+//  - cache characters as bitmap
+//  - try to update only changed parts of the stage!! This may require 
+//    additional code in the character instances, something like a 
+//    "invalidated" flag.
+//  - smart cache: start caching after 3 or 5 hyptothetical cache hits
+//    (hits to cache objects that contain no data, yet)
+ 
+
+/// This class holds a completely transformed path (fixed position). Speeds
+/// up characters that stay fixed on a certain position on the stage. 
+// ***CURRENTLY***NOT***USED***
+class agg_transformed_path 
+{
+  /// Original transformation matrix 
+  matrix m_mat;  
+  
+  /// Number of cache hits 
+  int hits;
+  
+  /// Number of cache misses
+  int misses;
+  
+  /// Contents of this cache item. First dimension is fill style 
+  std::vector <std::vector <agg::path_storage> > data;
+};
+
+class agg_cache_manager : private render_cache_manager
+{
+};
+
+
+// --- AGG HELPER CLASSES 
------------------------------------------------------
+
+// Style handler for AGG's compound rasterizer.
+class agg_style_handler
+{
+public:
+    agg_style_handler() : 
+        m_transparent(0, 0, 0, 0)
+    {}
+
+    /// Called by AGG to ask if a certain style is a solid color
+    bool is_solid(unsigned style) const { 
+      return true;  // The backend currently only supports solid fills 
+    }
+    
+    /// Adds a new solid fill color style
+    void add(const agg::rgba8 color) {
+      m_colors.push_back(color);
+    }
+
+    /// Returns the color of a certain fill style (solid)
+    const agg::rgba8& color(unsigned style) const 
+    {
+        if (style < m_colors.size())
+            return m_colors[style];
+
+        return m_transparent;
+    }
+
+    /// Called by AGG to generate a scanline span for non-solid fills 
+    void generate_span(agg::rgba8* span, int x, int y, unsigned len, unsigned 
style)
+    { 
+      // non-solid fills currently not supported
+    }
+
+
+private:
+    std::vector<agg::rgba8> m_colors;
+    agg::rgba8          m_transparent;
+};  // class agg_style_handler
+
+
+
+
+// --- RENDER HANDLER 
----------------------------------------------------------
+// The class is implemented using templates so that it supports any kind of
+// pixel format. LUT (look up tables) are not supported, however.
+
+template <class PixelFormat>
+class render_handler_agg : public render_handler
+{
+private:
+  typedef agg::renderer_base<PixelFormat> renderer_base;
+    
+  // TODO: Change these!!
+       unsigned char *memaddr;
+       int     memsize;
+       int xres;
+       int yres;
+       int bpp;        // bits per pixel
+       double scale;
+
+
+  // initialize AGG
+       void agg_init()
+       {
+       log_msg("agg_init()\n");
+
+       // set rendering buffer
+       m_rbuf.attach(memaddr, xres, yres, xres*(bpp/8));
+       
+       // clear screen
+       renderer_base rbase(*m_pixf);
+       rbase.clear(agg::rgba8(0, 0, 0));
+
+       log_msg("AGG initialized.\n");
+
+       //agg_drawtest();
+  }
+
+public:
+  int              m_view_width;      // TODO: remove these??
+  int              m_view_height;
+
+  // Enable/disable antialiasing.
+  bool m_enable_antialias;
+
+  // Output size.
+  float        m_display_width;
+  float        m_display_height;
+
+  gnash::matrix        m_current_matrix;
+  gnash::cxform        m_current_cxform;
+
+  void set_antialiased(bool enable) {
+               // dummy
+  }
+
+  // Style state.
+  enum style_index
+  {
+       LEFT_STYLE = 0,
+       RIGHT_STYLE,
+       LINE_STYLE,
+       STYLE_COUNT
+  };
+
+
+  gnash::bitmap_info*  create_bitmap_info_rgb(image::rgb* im)
+       // Given an image, returns a pointer to a bitmap_info class
+       // that can later be passed to fill_styleX_bitmap(), to set a
+       // bitmap fill style.
+       {          
+         UNUSED(im);
+         // bitmaps currently not supported! - return dummy for fontlib
+         return new bitmap_info_agg(); 
+    return NULL;
+       }
+
+
+  gnash::bitmap_info*  create_bitmap_info_rgba(image::rgba* im)
+       // Given an image, returns a pointer to a bitmap_info class
+       // that can later be passed to fill_style_bitmap(), to set a
+       // bitmap fill style.
+       //
+       // This version takes an image with an alpha channel.
+       {
+         UNUSED(im);
+         // bitmaps currently not supported! - return dummy for fontlib
+         return new bitmap_info_agg();
+    return NULL;
+       }
+
+
+  gnash::bitmap_info*  create_bitmap_info_empty()
+       // Create a placeholder bitmap_info.  Used when
+       // DO_NOT_LOAD_BITMAPS is set; then later on the host program
+       // can use movie_definition::get_bitmap_info_count() and
+       // movie_definition::get_bitmap_info() to stuff precomputed
+       // textures into these bitmap infos.
+       {
+         // bitmaps currently not supported! - return dummy for fontlib
+         return new bitmap_info_agg();
+    return NULL;
+       }
+
+  gnash::bitmap_info*  create_bitmap_info_alpha(int w, int h, uint8_t* data)
+       // Create a bitmap_info so that it contains an alpha texture
+       // with the given data (1 byte per texel).
+       {
+         UNUSED(w);
+    UNUSED(h);
+    UNUSED(data); 
+         // bitmaps currently not supported! - return dummy for fontlib
+         return new bitmap_info_agg();
+    return NULL;
+       }
+
+
+  void delete_bitmap_info(gnash::bitmap_info* bi)
+       // Delete the given bitmap info class.
+       {
+    free(bi);
+       }
+
+
+  // Constructor
+  render_handler_agg(unsigned char *mem, int size, int x, int y,
+       int bits_per_pixel)
+  {
+       memaddr = mem;
+       memsize = size;
+       xres            = x;
+       yres            = y;
+       bpp                     = bits_per_pixel;
+       scale           = 1/20.0;
+
+    // allocate pixel format accessor          
+    m_pixf = new PixelFormat(m_rbuf);
+    //m_rbase = new renderer_base(*m_pixf);  --> does not work!!??
+
+       agg_init();
+  }
+
+  // Destructor
+  ~render_handler_agg()
+  {
+
+  }
+
+  void begin_display(
+       gnash::rgba background_color,
+       int viewport_x0, int viewport_y0,
+       int viewport_width, int viewport_height,
+       float x0, float x1, float y0, float y1)
+       // Set up to render a full frame from a movie and fills the
+       // background.  Sets up necessary transforms, to scale the
+       // movie to fit within the given dimensions.  Call
+       // end_display() when you're done.
+       //
+       // The rectangle (viewport_x0, viewport_y0, viewport_x0 +
+       // viewport_width, viewport_y0 + viewport_height) defines the
+       // window coordinates taken up by the movie.
+       //
+       // The rectangle (x0, y0, x1, y1) defines the pixel
+       // coordinates of the movie that correspond to the viewport
+       // bounds.
+       {
+         UNUSED(viewport_x0);
+         UNUSED(viewport_y0);
+         UNUSED(viewport_width);
+         UNUSED(viewport_height);
+         UNUSED(x0);
+         UNUSED(y0);
+         UNUSED(x1);
+         UNUSED(y1);
+         
+         double scaleX, scaleY;
+         
+         // clear the stage using the background color   
+         renderer_base rbase(*m_pixf);
+    rbase.clear(agg::rgba8(background_color.m_r, background_color.m_g,
+       background_color.m_b, background_color.m_a));
+
+    // calculate final pixel scale
+    scaleX = (double)xres / (double)viewport_width / 20.0;  // 20=TWIPS
+    scaleY = (double)yres / (double)viewport_height / 20.0;
+    scale = scaleX<scaleY ? scaleX : scaleY;
+       }
+
+  bool allow_glyph_textures() {
+    // We want to render all glyphs in place 
+    return false; 
+  }
+
+  void end_display()
+       // Clean up after rendering a frame.  Client program is still
+       // responsible for calling glSwapBuffers() or whatever.
+       {
+    // nothing to do
+       }
+
+  void set_matrix(const gnash::matrix& m)
+       // Set the current transform for mesh & line-strip rendering.
+       {
+    // used only for drawing line strips...       
+         m_current_matrix = m;
+       }
+
+  void set_cxform(const gnash::cxform& cx)
+       // Set the current color transform for mesh & line-strip rendering.
+       {
+    // used only for drawing line strips...       
+    m_current_cxform = cx;
+       }
+
+  static void  apply_matrix(const gnash::matrix& m)
+       // add user space transformation
+       {
+    // TODO: what's the use for this, anyway?? 
+    log_msg("apply_matrix(); called - NOT IMPLEMENTED");
+       }
+
+  static void  apply_color(const gnash::rgba& c)
+       // Set the given color.
+       {
+    // TODO: what's the use for this, anyway?? 
+    log_msg("apply_color(); called - NOT IMPLEMENTED");
+       }
+
+
+  void line_style_width(float width)
+       {
+               line_width=width;
+       }
+
+
+
+  void draw_line_strip(const void* coords, int vertex_count, const rgba color)
+       // Draw the line strip formed by the sequence of points.
+       {
+    point pnt;
+    
+    renderer_base rbase(*m_pixf);
+
+       agg::scanline_p8 sl;
+       agg::rasterizer_scanline_aa<> ras;
+       agg::renderer_scanline_aa_solid<
+       agg::renderer_base<PixelFormat> > ren_sl(rbase);
+
+    agg::path_storage path;
+    agg::conv_stroke<agg::path_storage> stroke(path);
+    stroke.width(1);
+    stroke.line_cap(agg::round_cap);
+    stroke.line_join(agg::round_join);
+    path.remove_all(); // Not obligatory in this case
+
+    const int16_t *vertex = (int16_t*)coords;
+    
+    m_current_matrix.transform(&pnt, point(vertex[0], vertex[1]));
+       path.move_to(pnt.m_x * scale, pnt.m_y * scale);
+
+    for (vertex += 2;  vertex_count > 1;  vertex_count--, vertex += 2) {
+      m_current_matrix.transform(&pnt, point(vertex[0], vertex[1]));
+       path.line_to(pnt.m_x * scale, pnt.m_y * scale);
+    }
+               // The vectorial pipeline
+       ras.add_path(stroke);
+
+       // Set the color and render the scanlines
+       ren_sl.color(agg::rgba8(color.m_r, color.m_g, color.m_b, color.m_a));
+       agg::render_scanlines(ras, sl, ren_sl);
+
+       } // draw_line_strip
+
+
+  void draw_bitmap(
+       const gnash::matrix& m,
+       const gnash::bitmap_info* bi,
+       const gnash::rect& coords,
+       const gnash::rect& uv_coords,
+       gnash::rgba color)
+       // Draw a rectangle textured with the given bitmap, with the
+       // given color.  Apply given transform; ignore any currently
+       // set transforms.
+       //
+       // Intended for textured glyph rendering.
+       {
+         UNUSED(color);
+    log_msg("  draw_bitmap NOT IMPLEMENTED\n");
+       }
+
+  void begin_submit_mask()
+       {
+    // not implemented
+       }
+
+  void end_submit_mask()
+       {
+    // not implemented
+       }
+
+  void disable_mask()
+       {
+    // not implemented
+       }
+       
+
+
+  /*
+   Takes a list of paths and combines them according to their fill style so 
+   that each path is a closed polygon. Each path will have exactly one fill 
+   style.
+   This is necessary because Flash uses up to two fill styles for each path,
+   one for each side. It can happen that a path contains only one edge. In that
+   case it just divides a shape in two (two colors). Since this is impossible
+   to draw directly we need to restore the original polygons so that they can
+   be drawn independently.
+   
+   IMPORTANT NOTE: This is currently *not* used for AGG anymore, because AGG
+   has a direct rasterizer for double styles...   
+   */   
+  void combine_paths(std::vector<path> &paths_in, std::vector<path> 
&paths_out) {
+    
+    int ino;      // index of input path
+    int incount;  // cache value for paths_in.size()
+    int ono;      // index of output path
+    int eno;      // edge index 
+    
+    #define EQUAL(a,b) (fabs(a-b)<0.000000001)
+    
+    incount = paths_in.size();
+    
+    /*
+    Strategy: For fill style 0, each path is compared with other paths sharing
+    the same fill style and it is tried to attach the new path to a already 
+    known path. The start point of the new path must match the end point of the
+    previous path. Only distant shapes will not find a matching path and thus
+    will create a new one.
+    For fill style 1 the same is done except that the path is reversed first.
+    The resulting paths will use exactly one fill style (fill style 0).
+    Each source path may be used for two different resulting shapes (one time
+    normally and the other reversed).    
+    */
+    
+    // browse through all paths...
+    for (ino=0; ino<incount; ino++) {
+    
+      path &new_path = paths_in[ino];
+      int found;
+
+      // === FILL STYLE 0 ===
+      
+      if (new_path.m_fill0) {
+        found=0;
+        
+        // Search paths sharing the same fill style and whose end point matches
+        // the START point of <new_path>
+        for (ono=0; ono < paths_out.size(); ono++) {
+        
+          path &cp = paths_out[ono];
+          edge &last_edge = cp.m_edges.back();
+          
+          log_msg("  = [FS0] compare style %d with %d: ", new_path.m_fill0,
+            paths_out[ono].m_fill0);
+          if (new_path.m_fill0 != paths_out[ono].m_fill0) {
+            log_msg("no match\n"); 
+            continue;  // fill style mismatch
+          }
+          log_msg("match!\n");
+            
+          log_msg("  = [FS0] compare edge %f/%f with %f/%f: ", 
+            new_path.m_ax, new_path.m_ay,
+            last_edge.m_ax, last_edge.m_ay);  
+          if (!EQUAL(new_path.m_ax, last_edge.m_ax) ||
+              !EQUAL(new_path.m_ay, last_edge.m_ay)) {
+            log_msg("no match\n");
+            continue;  // cannot attach
+          }
+          log_msg("match!\n");
+            
+            
+          // ==> ok, attach "this_path" to "cp"
+          
+          log_msg(" == [FS0] attach path #%d\n", ino);
+           
+          // TODO: Resize vector and set elements directly, avoiding multiple
+          // resizing...
+          //cp->resize( cp->size() + new_path->size() );
+          
+          for (eno=0; eno<new_path.m_edges.size(); eno++) {
+            cp.m_edges.push_back(new_path.m_edges[eno]);
+          }
+          
+          found=1;
+          
+          break;
+        
+        }  // for ono
+        
+        if (!found) {
+          // We found no matching path, so start a new one...
+          log_msg(" == [FS0] start new path #%d\n", ino);
+          path temp = new_path;
+          temp.m_fill1=0;   // use only fill style 0
+          paths_out.push_back(temp);
+        }
+
+      } // if m_fill0
+      
+    
+      // === FILL STYLE 1 ===
+      
+      if (new_path.m_fill1) {
+        float last_cx;
+        float last_cy;
+        float next_cx;
+        float next_cy;
+        found=0;
+        
+        // create a new, reversed path
+        path rev_path;
+        rev_path.m_fill0 = new_path.m_fill1;
+        rev_path.m_ax = new_path.m_edges.back().m_ax;
+        rev_path.m_ay = new_path.m_edges.back().m_ay;
+        last_cx = new_path.m_edges.back().m_cx;
+        last_cy = new_path.m_edges.back().m_cy;
+        for (eno=new_path.m_edges.size()-2; eno>=0; eno--) {  
+          edge temp = new_path.m_edges[eno];
+          next_cx = temp.m_cx;
+          next_cy = temp.m_cy;
+          temp.m_cx = last_cx;
+          temp.m_cy = last_cy;
+          rev_path.m_edges.push_back(temp);
+          last_cx=next_cx;
+          last_cy=next_cy;
+        }
+        
+        {
+          edge temp;
+          temp.m_ax = new_path.m_ax;
+          temp.m_ay = new_path.m_ay;
+          temp.m_cx = last_cx;
+          temp.m_cy = last_cy;
+          rev_path.m_edges.push_back(temp);   // add anchor of new_path as 
last edge
+        }
+        
+        
+        
+        // ==> now proceed just as like with fill style 0...
+        
+        
+        
+        // Search paths sharing the same fill style and whose end point matches
+        // the START point of <new_path>
+        for (ono=0; ono < paths_out.size(); ono++) {
+        
+          path &cp = paths_out[ono];
+          edge &last_edge = cp.m_edges.back();
+          
+          log_msg("  = [FS1] compare style %d with %d: ", rev_path.m_fill0,
+            paths_out[ono].m_fill0);
+          if (rev_path.m_fill0 != paths_out[ono].m_fill0) {
+            log_msg("no match\n"); 
+            continue;  // fill style mismatch
+          }
+          log_msg("match!\n");
+            
+          log_msg("  = [FS1] compare edge %f/%f with %f/%f: ", 
+            rev_path.m_ax, rev_path.m_ay,
+            last_edge.m_ax, last_edge.m_ay);  
+          if (!EQUAL(rev_path.m_ax, last_edge.m_ax) ||
+              !EQUAL(rev_path.m_ay, last_edge.m_ay)) {
+            log_msg("no match\n");
+            continue;  // cannot attach
+          }
+          log_msg("match!\n");
+            
+            
+          // ==> ok, attach "this_path" to "cp"
+          
+          log_msg(" == [FS1] attach path #%d\n", ino);
+           
+          // TODO: Resize vector and set elements directly, avoiding multiple
+          // resizing...
+          //cp->resize( cp->size() + new_path->size() );
+          
+          for (eno=0; eno<rev_path.m_edges.size(); eno++) {
+            cp.m_edges.push_back(rev_path.m_edges[eno]);
+          }
+          
+          found=1;
+          
+          break;
+        
+        }  // for ono
+        
+        if (!found) {
+          // We found no matching path, so start a new one...
+          log_msg(" == [FS1] start new path #%d\n", ino);
+          path temp = rev_path;
+          temp.m_fill1=0;   // use only fill style 0
+          paths_out.push_back(temp);
+        }
+
+      }
+
+    
+    }
+    
+    #undef EQUAL   
+    
+  }
+
+
+
+  void draw_glyph(shape_character_def *def,
+      const matrix& mat, rgba color, float pixel_scale) {
+      
+    // create a new path with the matrix applied   
+    std::vector<path> paths;    
+    apply_matrix_to_path(def->get_paths(), paths, mat);
+      
+    // make sure m_single_fill_styles contains the required color 
+    need_single_fill_style(color);
+
+    // draw the shape
+    draw_shape(paths, m_single_fill_styles, m_neutral_cxform, false);
+    
+    // NOTE: Do not use even-odd filling rule for glyphs!
+  }
+
+
+  void draw_shape_character(shape_character_def *def, 
+    const matrix& mat,
+    const cxform& cx,
+    float pixel_scale,
+    const std::vector<fill_style>& fill_styles,
+    const std::vector<line_style>& line_styles) {
+    
+    std::vector<path> paths;
+    
+    apply_matrix_to_path(def->get_paths(), paths, mat);
+    
+    draw_shape(paths, fill_styles, cx, true);
+    
+    draw_outlines(paths, line_styles, cx);
+  }
+
+
+  /// Takes a path and translates it using the given matrix. The new path
+  /// is stored in paths_out.  
+  void apply_matrix_to_path(const std::vector<path> &paths_in, 
+    std::vector<path> &paths_out, const matrix &mat) {
+    
+    int pcount, ecount;
+    int pno, eno;
+    
+    // copy path
+    paths_out = paths_in;    
+    pcount = paths_out.size();
+        
+    
+    for (pno=0; pno<pcount; pno++) {
+    
+      path &the_path = paths_out[pno];     
+      point oldpnt(the_path.m_ax, the_path.m_ay);
+      point newpnt;
+       
+      mat.transform(&newpnt, oldpnt);
+      the_path.m_ax = newpnt.m_x;    
+      the_path.m_ay = newpnt.m_y;
+      
+      ecount = the_path.m_edges.size();
+      for (eno=0; eno<ecount; eno++) {
+      
+        edge &the_edge = the_path.m_edges[eno];
+        
+        oldpnt.m_x = the_edge.m_ax;
+        oldpnt.m_y = the_edge.m_ay;
+        mat.transform(&newpnt, oldpnt);
+        the_edge.m_ax = newpnt.m_x;
+        the_edge.m_ay = newpnt.m_y;
+        
+        oldpnt.m_x = the_edge.m_cx;
+        oldpnt.m_y = the_edge.m_cy;
+        mat.transform(&newpnt, oldpnt);
+        the_edge.m_cx = newpnt.m_x;
+        the_edge.m_cy = newpnt.m_y;
+      
+      }          
+      
+    } 
+    
+  } // apply_matrix
+
+
+  /// 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).
+  void draw_shape(const std::vector<path> &paths,
+    const std::vector<fill_style> &fill_styles, const cxform& cx, int 
even_odd) {
+    
+    /*
+    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...
+    */
+    
+
+    // Gnash stuff 
+    int pno, eno, fno;
+    int pcount, ecount, fcount;
+    
+    // AGG stuff
+    renderer_base rbase(*m_pixf);
+    agg::scanline_u8 sl;                // scanline renderer
+    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
+    
+    // debug
+    int edge_count=0;
+    
+    // activate even-odd filling rule
+    if (even_odd)
+      rasc.filling_rule(agg::fill_even_odd);
+    else
+      rasc.filling_rule(agg::fill_non_zero);
+  
+      
+    // tell AGG what styles are used
+    fcount = fill_styles.size();
+    //log_msg("%d fill styles\n", fcount);
+    for (fno=0; fno<fcount; fno++) {
+      rgba color = cx.transform(fill_styles[fno].get_color());
+      
+      // add the color to our self-made style handler (basically just a list)
+      sh.add(agg::rgba8(color.m_r, color.m_g, color.m_b, color.m_a)); 
+    }
+    
+      
+    // push paths to AGG
+    pcount = paths.size();
+    //log_msg("%d paths\n", pcount);
+    for (pno=0; pno<pcount; pno++) {
+    
+      const path &this_path = paths[pno];
+      agg::path_storage path;
+      agg::conv_curve< agg::path_storage > curve(path);
+    
+      // 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*scale, this_path.m_ay*scale);      
+      
+      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*scale, this_edge.m_ay*scale);
+        else
+          path.curve3(this_edge.m_cx*scale, this_edge.m_cy*scale,
+                      this_edge.m_ax*scale, this_edge.m_ay*scale);
+        
+      }
+      
+      // add path to the compound rasterizer
+      rasc.add_path(curve); 
+    
+    }
+    //log_msg("%d edges\n", edge_count);
+    
+    // render!
+    agg::render_scanlines_compound_layered(rasc, sl, rbase, alloc, sh);
+    
+  } // draw_shape
+
+
+
+  /// Just like draw_shapes() except that it draws an outline.
+  void draw_outlines(const std::vector<path> &paths,
+    const std::vector<line_style> &line_styles, const cxform& cx) {
+    
+    // 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;
+    
+    // AGG stuff
+    renderer_base rbase(*m_pixf);
+    agg::scanline_p8 sl;                // scanline renderer
+    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
+    
+    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
+    
+    
+    pcount = paths.size();   
+    for (pno=0; pno<pcount; pno++) {
+      
+      const path &this_path = paths[pno];
+      
+      if (!this_path.m_line)  
+        continue;     // invisible line
+        
+      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*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*scale, this_path.m_ay*scale);
+        
+      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*scale, this_edge.m_ay*scale);
+        else
+          agg_path.curve3(this_edge.m_cx*scale, this_edge.m_cy*scale,
+                      this_edge.m_ax*scale, this_edge.m_ay*scale);
+        
+      } // for edges
+      
+      
+      ras.add_path(stroke);
+      ren_sl.color(agg::rgba8(color.m_r, color.m_g, color.m_b, color.m_a));
+      
+      agg::render_scanlines(ras, sl, ren_sl);
+    
+    
+    }
+      
+  } // draw_outlines
+
+
+  
+  /// Draws the given polygon.
+  void  draw_poly(const point* corners, int corner_count, const rgba fill, 
+    const rgba outline) {
+    
+    if (corner_count<1) return;
+    
+    // TODO: Use aliased scanline renderer instead of anti-aliased one since
+    // it is undesired anyway.
+    renderer_base rbase(*m_pixf);
+    agg::scanline_p8 sl;
+    agg::rasterizer_scanline_aa<> ras;
+    agg::renderer_scanline_aa_solid<
+      agg::renderer_base<PixelFormat> > ren_sl(rbase);
+      
+    agg::path_storage path;
+    point pnt, origin;
+    
+    // Note: The coordinates are rounded and 0.5 is added to snap them to the 
+    // center of the pixel. This avoids blurring caused by anti-aliasing.
+    
+    m_current_matrix.transform(&origin, 
+      point(trunc(corners[0].m_x), trunc(corners[0].m_y)));
+    path.move_to(trunc(origin.m_x*scale)+0.5, trunc(origin.m_y*scale)+0.5);
+    
+    for (int i=1; i<corner_count; i++) {
+    
+      m_current_matrix.transform(&pnt, point(corners[i].m_x, corners[i].m_y));
+        
+      path.line_to(trunc(pnt.m_x*scale)+0.5, trunc(pnt.m_y*scale)+0.5);
+    }
+    
+    // close polygon
+    path.line_to(trunc(origin.m_x*scale)+0.5, trunc(origin.m_y*scale)+0.5);
+    
+    // commit path
+    ras.add_path(path);
+    
+    // fill polygon
+    if (fill.m_a>0) {
+      ren_sl.color(agg::rgba8(fill.m_r, fill.m_g, fill.m_b, fill.m_a));
+      agg::render_scanlines(ras, sl, ren_sl);
+    }
+    
+    // draw outline
+    if (outline.m_a>0) {
+      agg::conv_stroke<agg::path_storage> stroke(path);
+      
+      stroke.width(1);
+      
+      ren_sl.color(agg::rgba8(outline.m_r, outline.m_g, outline.m_b, 
outline.m_a));
+      
+      ras.add_path(stroke);
+      agg::render_scanlines(ras, sl, ren_sl);
+    }
+    
+  }
+                      
+                      
+  /*
+  This method is *not* being used anymore, because we use a special AGG 
+  rasterizer that can deal with Flash edges directly. 
+  It is kept here since it was hard work and who knows it it becomes useful
+  again some day. 
+  It works for 95% of the shapes. There is just a special case where it does
+  not draw a curve when it should do so. I guess there is some bug in the
+  combine_paths() method when reversing a path. Probably control point of the
+  first or last edge of a path is wrong (gets lost) somehow.  
+  */
+  void draw_shape_character_old(shape_character_def *def, 
+    character *inst) {
+    
+    // replaces: shape_character_def::display()   (both versions)
+    // replaces: shape_character_def::tesselate() 
+
+    // Gnash stuff
+    std::vector<path> paths, orig_paths;
+    std::vector<fill_style> fill_styles;
+    path current_path;
+    int paths_count;
+    int pathno, edgeno, edge_count;    
+    rgba color;
+    int fillno;
+    int fillidx;
+    
+    // AGG stuff
+    PixelFormat pixf(rbuf);
+    renderer_base rbase(pixf);
+    agg::scanline_p8 sl;
+    agg::rasterizer_scanline_aa<> ras;
+    agg::renderer_scanline_aa_solid<
+      agg::renderer_base<PixelFormat> > ren_sl(rbase);
+     
+    // We need one AGG path (polygon) for each fill style
+    std::vector< agg::path_storage > agg_paths; 
+
+    log_msg("draw_shape_character() called.\n");
+    
+    /*
+    
+    Flash will tend to save outlines in clockwise rotating order.
+    We use the "non zero" filling rule of AGG, that is, when you raw a 
rectangle 
+    in clockwise order and another rectangle inside the other with 
+    counter-clockwise order, it will keep the inner rectangle transparent.
+    
+    AGG could also do the "even odd" filling rule where the order does not 
+    matter (so we don't need to reverse fill style #1), however this leads to
+    noticeable borders between polygons. Don't know exactly why, however the
+    typical fill style text (Flash SDK) shows two green triangles instead of
+    a green rectangle. 
+    
+    So, we simple draw all paths for fill style 1 in reverse order, as opposed
+    to fill style 0, which is drawn normally. 
+    
+    */
+    ras.filling_rule(agg::fill_non_zero);
+    //ras.filling_rule(agg::fill_even_odd);
+    
+    
+    //--
+    
+    // Combine paths, so that they are easier to draw
+    orig_paths = def->get_paths();
+    combine_paths(orig_paths, paths);
+      
+  
+    
+    fill_styles = def->get_fill_styles();
+    
+    paths_count = paths.size();     // fasten access
+    
+    log_msg("Fill styles vector contains %d items.\n", fill_styles.size());
+    
+    log_msg("Preparing paths vector...\n");
+    agg_paths.resize(fill_styles.size());
+    
+    log_msg("Paths vector contains %d items.\n", paths_count);
+    
+    for (pathno=0; pathno<paths_count; pathno++) {
+    
+      log_msg("Processing path #%d\n", pathno);
+      
+      current_path = paths[pathno];
+      
+      log_msg("  path fill0 has index %d\n", current_path.m_fill0);
+      log_msg("  path fill1 has index %d\n", current_path.m_fill1);
+      
+      log_msg("  path anchor at %f / %f\n", 
+        current_path.m_ax/20, current_path.m_ay/20);
+
+      edge_count = current_path.m_edges.size();      
+      log_msg("  path has %d edges.\n", edge_count);
+      
+
+      //=== DRAW FILL STYLE 0 IN NORMAL ORDER ===
+
+      fillidx = current_path.m_fill0-1;
+      
+      if (fillidx>=0) {
+      
+        agg::path_storage *the_path = &agg_paths[fillidx];
+        
+        log_msg("  adding path for fill style 0 (normal)\n");
+
+        the_path->move_to(current_path.m_ax*scale, current_path.m_ay*scale);   
   
+        
+        for (edgeno=0; edgeno<edge_count; edgeno++) {
+          log_msg("    edge #%d anchor %f/%f control %f/%f\n", edgeno,
+            current_path.m_edges[edgeno].m_ax/20,
+            current_path.m_edges[edgeno].m_ay/20,
+            current_path.m_edges[edgeno].m_cx/20,
+            current_path.m_edges[edgeno].m_cy/20);
+            
+          if (current_path.m_edges[edgeno].is_straight()) {
+            the_path->line_to(current_path.m_edges[edgeno].m_ax*scale, 
+              current_path.m_edges[edgeno].m_ay*scale);
+          }
+          else 
+          {
+            log_msg("    drawing curve\n");
+            the_path->curve3(
+              current_path.m_edges[edgeno].m_cx*scale, 
+              current_path.m_edges[edgeno].m_cy*scale,
+              current_path.m_edges[edgeno].m_ax*scale, 
+              current_path.m_edges[edgeno].m_ay*scale);
+          }
+        } // for edge
+        
+        
+      } // if fillidx
+
+
+
+      //=== DRAW FILL STYLE 1 IN REVERSED ORDER ===
+
+      fillidx = current_path.m_fill1-1;
+      
+      if (fillidx>=0) {
+      
+        float next_ax, next_ay, next_cx, next_cy;
+        float last_ax, last_ay, last_cx, last_cy;
+      
+        agg::path_storage *the_path = &agg_paths[fillidx];
+        
+        log_msg("  adding path for fill style 1 (reversed)\n");
+
+        /*the_path->move_to(current_path.m_edges[edge_count-1].m_ax*scale, 
+                         current_path.m_edges[edge_count-1].m_ay*scale);*/
+                         
+        last_ax = current_path.m_ax*scale;       
+        last_ay = current_path.m_ay*scale;
+        last_cx = last_ax;        
+        last_cy = last_ay;
+        the_path->move_to(last_ax, last_ay);      
+        
+        for (edgeno=edge_count-1; edgeno>=0; edgeno--) {
+          log_msg("    edge #%d anchor %f/%f control %f/%f\n", edgeno,
+            current_path.m_edges[edgeno].m_ax/20,
+            current_path.m_edges[edgeno].m_ay/20,
+            current_path.m_edges[edgeno].m_cx/20,
+            current_path.m_edges[edgeno].m_cy/20);
+            
+          next_ax = current_path.m_edges[edgeno].m_ax*scale;
+          next_ay = current_path.m_edges[edgeno].m_ay*scale;
+          next_cx = current_path.m_edges[edgeno].m_cx*scale;
+          next_cy = current_path.m_edges[edgeno].m_cy*scale;
+          
+          log_msg("      the_path->curve3(%f, %f, %f, %f);\n", last_cx, 
last_cy, next_ax, next_ay);
+          the_path->curve3(last_cx, last_cy, next_ax, next_ay);
+          
+          // TODO: Do not add a curve when it is a straight line
+          
+          last_cx = next_cx;
+          last_cy = next_cy;
+          
+        } // for edge
+        
+        the_path->curve3(last_cx, last_cy, current_path.m_ax*scale, 
current_path.m_ay*scale);      
+        // TODO: Do not add a curve when it is a straight line
+
+      }
+                              
+    } // for path
+    
+    
+    /*
+    Ok, now we have prepared all the paths for all fill styles. Note that AGG
+    won't render curves unless we use conv_curve. The next step is to feed the
+    single paths to AGG with the right color.
+    Some paths may be empty (unused fill styles).    
+    */
+    
+    for (fillidx=0; fillidx<fill_styles.size(); fillidx++) {
+    
+      agg::conv_curve< agg::path_storage > curve(agg_paths[fillidx]);
+    
+      log_msg("  drawing fill style #%d ...\n", fillidx);     
+    
+      color = fill_styles[fillidx].get_color();
+      
+      log_msg("    color is R/G/B/A %d/%d/%d/%d\n", 
+        color.m_r, color.m_g, color.m_b, color.m_a);
+      
+      ren_sl.color(agg::rgba8(color.m_r, color.m_g, color.m_b, color.m_a));    
+          
+      ras.add_path(curve);
+    
+      agg::render_scanlines(ras, sl, ren_sl);
+      usleep(1000000);
+     }
+     
+      
+  }    // draw_shape_character_old
+
+  
+private:  // private methods  
+
+  /// Returns the cache manager instance of the given character definition.
+  /// Allocates a new manager if necessary.
+  agg_cache_manager* get_cache_of(character* def) {
+  
+    if (def->m_render_cache == NULL) {
+      def->m_render_cache = new agg_cache_manager;
+    }
+    
+    return def->m_render_cache;
+  
+  }
+  
+private:  // private variables
+
+  agg::rendering_buffer m_rbuf;  
+  PixelFormat *m_pixf;
+  
+};     // end class render_handler_agg
+
+
+
+
+// TODO: Replace "pixelformat" with a enum!
+
+DSOEXPORT render_handler*      create_render_handler_agg(char *pixelformat, 
+  unsigned char *mem, int memsize, int xres, int yres, int bpp)
+{
+
+  log_msg("framebuffer pixel format is %s", pixelformat);
+
+  if (!strcmp(pixelformat, "RGB555"))
+         return new render_handler_agg<agg::pixfmt_rgb555> (mem, memsize, 
xres, yres, bpp);
+       
+       else if (!strcmp(pixelformat, "RGB565"))
+         return new render_handler_agg<agg::pixfmt_rgb565> (mem, memsize, 
xres, yres, bpp);
+       
+       else if (!strcmp(pixelformat, "RGB24"))
+         return new render_handler_agg<agg::pixfmt_rgb24> (mem, memsize, xres, 
yres, bpp);
+               
+       else if (!strcmp(pixelformat, "BGR24"))
+         return new render_handler_agg<agg::pixfmt_rgb565> (mem, memsize, 
xres, yres, bpp);
+         
+       else assert(0);
+       
+       return NULL; // avoid compiler warning
+}
+
+} // end of namespace gnash
+
+
+// Local Variables:
+// mode: C++
+// indent-tabs-mode: t
+// End:
+/* vim: set cindent tabstop=8 softtabstop=4 shiftwidth=4: */




reply via email to

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