Thanks a lot! Some notes by myself [...] > Player > ------ > > There seem to be two gnash.cpp, one in gui/ and one in backend/, the latter > being more than twice as big. However, it appears that the one in gui/ is the > correct one because there has been some redesign of the internal Gnash > architecture (support for multiple front-/backends). I confirm backend/gnash.cpp is obsoleted, and should actually be gone from CVS now. > gnash.cpp is used for both the standalone and the plugin mode. The plugin simply makes a system call to the 'gnash' executable, no matter what the sources for it are :) The plugin source is plugin/plugin.cpp [...] > The sound handlers are then being intialized, but I won't go any deeper there > because it's currently far from my interest... > > get_movie_info() (from server/impl.cpp) is then used to read the header of the > SWF file. The size of the stage, frame rate etc. are retrieved this way. The > function raises an exception when an error occurrs. Compressed SWF files seem > to be supported (using ZLIB). IFF you do have ZLIB, compressed SWF files *are* supported. BTW, I was thinking about removing that call as it triggers double stream creatino (once for get_movie_info and once for create_library_movie). Movie info should be fetched from an already opened stream IMHO... > There is a special code fragment for the SDL GUI which calls a disableCoreTrap() > function - probably a quick hack not really belonging there? Most likely... > The command line arguments are passed to the GUI, so that it may extract > additional settings from it (?). I think this was the idea, but I don't think any gui is currently doing it. [..] > Using set_current_root() a global variable "s_current_root" is updated which > probably reflects the current root MC ("_root" in Flash). This variable may be > updated when an external movie is loaded and processed (via LoadMovieClip Flash > command I guess). I think set_current_root() should only be called when the uppermost movie is replaced with something else. The "_root" reference in Flash is a "relative" one. Ie. you can have multiple "_root" movies in a single run: one for each loaded SWF. [...] > The GUI > ------- > > I'll continue my analysis using the GTK GUI and the Cairo backend. > > The class descendant is defined in gtksup.h and implemented in gtk.cpp. It > does all the necessary stuff to initialize a GTK window. > > A class member named "glue" is the interface to the backend, in this case being > the GtkCairoGlue class. It is initialized in GtkGui::Init (again passing command > line parameters). > > The "Gui" base class already defines a member called "_renderer" which is the > interface to the renderer class. The appropriate instance is created in > GtkGui::createWindow(), which in turn is called from within the main program. > > The file "gtk_glue_cairo.cpp" apparently is the connection between the GUI and > the backend. It contains the actual code to *create* the renderer instance by > calling itself gdk_cairo_create(). It doesn't do much more useful and I did not > yet find the reason for the "glue" class (just to avoid a few IFDEFs?). > > For clarity, here is the complete call stack for creating the renderer: > > gui/gnash.cpp -> gui.createWindow(infile, width, height); > | > gui/gtk.cpp -> _renderer = glue.createRenderHandler(); > | > gui/gtk_glue_cairo.cpp -> return create_render_handler_cairo(...); > | > backend/render_handler_cairo.cpp -> return new render_handler_cairo(); > > The frame delay (setCallback(), see above) is implemented using a GTK timer with > low priority that calls the method "advance_movie" from the base Gui class (see > gui/gui.cpp). > > The GUI (in the "run" method) passes control to GTK via gtk_main(), which will > take care of displaying it's window and call the "advance_movie" method > periodically. It will not exit before the application should be terminated > (gtk_quit() probably). > > advance_movie() itself advances the root movie (MC) by one frame. The parameter > of server/movie_instance.cpp:advance() is a float which I don't understand > why. There are a bunch of stacked advanceXXXX() calls but generally a frame > advace would always be 1.0. Maybe floats are required for tweening where it > would make sense, but I haven't checked this. IIRC the float is the "time-left". I don't think it's working, but theoretically it would skip something if run out of time. > Anyway, movie_interface::advance() probably has the following (unchecked) tasks: > > - update the display list (that is, what is visible on the screen) according > to the new frame > > - apply tweening > > - process ActionScript code > > - define the current viewport? > > It then calls display() of the root movie (movie_root class). That method calls > begin_display() of the renderer class and passes the current viewport, the > background color and the frame (=stage) size. The renderer normally clears the > framebuffer by filling the viewport with the background color. > > Then the more generic display() method (of movie_interface, or more exactly > sprite_instance) is called, which draws the display list and finally > end_display() is told to the renderer class to commit the current frame. > > More on the display() method in the next section! > > Then gui::renderBuffer() is called, which again is located in gtk.cpp and passes > the call to glue::render(), which in the case of the Cairo backend does nothing. > This may be a function that can be used to swap a double buffer or tell > something to acceleration hardware but it seems that it's just a duplicate for > end_display(), except that the call goes through the GUI and so may be used to > blit the frame buffer on screen or something... > > NOTE: This function probably could be very useful for the AGG backend. The > backend would render the frame the same way for any platform / WM combination. > The GUI then takes the finished frame buffer and blits it to the hardware > framebuffer (/dev/fb0), to some X window, or Windows GDI handle, or whatever. > This last step would fit well into renderBuffer() directly. > > > Rendering / Sprite instances > ---------------------------- > > Back to the sprite_instance::display() method. > > The sprite_instance class holds information for any existing (visible and > invisible) sprite on the stage. In the Flash world this would be a MovieClip > (MC) which itself can contain other MCs and stateful information such as > ActionScript variables. The root MC is basically a sprite too, just like any > MC has it's own timeline. > > sprite_instance class inheritance: > > > ref_counted (server/ref_counted.h) > | > as_object (server/as_object.h) > | > movie_interface (server/movie_interface.h) > | > movie (server/movie.h) > | > character (server/character.h) > | > sprite_instance (server/sprite_instance.h) ... I hate the "movie_interface - movie" part, trying to get rid of it. BTW, we should also get rid of ref_counted base... As you can see it is an 'instance' being displaied, not a definition. I'd like this to be true for all classes. The point is that a definition is *fixed*, while an instance can change at runtime (transformed, moved) > The sprite contains a member "m_display_list" which is of type "DisplayList" > (defined in server/dlist.h). This is the display list of the sprite, that is, > what "characters" the sprite itself does contain. Each character has it's own > "depth" which defines the order of which the characters have to be displayed. > No two characters can be at the same depth. DisplayList is one of the new classes (I rewrote it). I guess you can tell by naming convention. The aim is to split existing classes in smaller classes so it's easier to move them around. > sprite_instance::display() passes the call to display_list::display() which > goes through the list of characters and calls their own display() method. > Since MCs can contain mask layers the sprite may inform the renderer about it. > > Basically, before a character of a mask is rendered (the mask "begins"), the > method renderer::begin_submit_mask() is called. Then normal rendering > operations follow until the mask is confirmed with renderer::end_submit_mask(). > This enables the mask which remains active until render::disable_mask() is > called. > > Say, we have a rectangle A partially masked by circle B and a triangle C that > does not have a mask (above it). This should work as follows: > > 1. renderer::begin_submit_mask() > 2. circle B is drawn > 3. renderer::end_submit_mask() --> the mask is now a circle > 4. rectangle A is drawn, using the mask > 5. render::disable_mask() --> mask not required anymore > 6. triangle C is drawn normally > > The way how the specific character (which may be a MC of it's own) is being > drawen depends on the character type (see following sections). > > > Outlines and shapes > ------------------- > > The file server/shape.h contains a few classes that are drawing primitives > (curves, shapes, ...). The'yre created while parsing the SWF file. You mentioned we would be using these classes from the drawing API, right ? So this might belong to a 'shapes' lib (or geometry lib, which we already have) > When a line or curve is read from the SWF file it is being handled by > server/parser/shape_character_def.cpp > > It's read() method first loads the fill and line style. For a simple outline, > there would be no fill. Then it reads the actual shape records. > > NOTE: Any shape (being it filled or not) begins in the SWF file with the > definiton of the fill styles, followed by the line styles and finally the > edges that define the shape itself. Along the edges path the previously > defined styles may be selected at any position. > > Any shape consists of straight lines and/or curves. > > So read(): > - calls read_fill_styles() to read all the necessary fill styles of the shape > - calls read_line_styles() to do the same for the line styles > - reads the number of bits used for indexing fill and line styles later on > - loads the shape definition and stores it in the "current_path" variable > > The current_path variable (of type "path") contains a list of objects of type > "edge". Straight lines are also stored as curves, whose control point equals > the anchor point. So, the path only contains a list of curves! > > At the end the current_path is added to the class' "m_paths" vector. > > The display() method of the class takes care that the path of the shape is > converted to a valid "mesh", that is a set of straight lines. This mesh is also I think it should be the display() method of a shape_character_instance instead. > stored in some sort of cache so that it does not have to be re-calculated when > it's not necessary (important speed optimization). The cache invalidation should be automatic whenever the shape_instance is updated (a matrix transfor applied, a new line being drawn, a clear, etc.) > Of course these straight lines are an approximation of the curves and so the > display() method keeps track of the error. If it raises a certain tolerance, > then the mesh needs to be recalculated. This may be necessary when the total > transformation matrix changes noticeably, for example when resizing a sprite. > > The class "mesh_set" takes care of this (more on this later). > > Regardless of whether the mesh has been loaded from cache or just recalculated, > it's own display() method is called to render it. Thus, I'd have the mesh_set an utility class for the shape_character_instance, possibly only visible by it's implementation, to simplify code browsing. [..] That a great job you've been doing. I suggest writing these info as Doxygen notes and also in the internals.xml file. Thank you.