Author: miguel
Date: 2007-05-26 15:17:39 -0400 (Sat, 26 May 2007)
New Revision: 77991
Added:
trunk/moon/src/demo.cpp
trunk/moon/src/runtime.cpp
trunk/moon/src/video.cpp
Removed:
trunk/moon/src/demo.c
trunk/moon/src/runtime.c
trunk/moon/src/video.c
Modified:
trunk/moon/README
trunk/moon/src/Makefile.am
trunk/moon/src/runtime.h
Log:
move to C++
Modified: trunk/moon/README
===================================================================
--- trunk/moon/README 2007-05-26 16:56:17 UTC (rev 77990)
+++ trunk/moon/README 2007-05-26 19:17:39 UTC (rev 77991)
@@ -7,21 +7,24 @@
This is not likely the final version of this code.
-* Object System
+* Use of C++
- This contains a poor man's object system.
+ C++ is only used for the class hierarchy and API entry points
+ are not exposed as C++ member methods but instead they are exposed
+ as regular C-callable methods that have access to the C++
+ public fields or any other public inline declarations.
- The vtable is the first field in the base object (Item in
- this case) and is encoded as a void *. You must cast this
- to ItemVtable to be able to call methods. I do not remember
- why I did not just go for the hardcoded ItemVtable there.
+ Object creation uses the gtk-like naming scheme for constructors:
- Should be fixed perhaps.
+ ClassName *class_name_new ();
- The various XXX_init() methods are here merely to initialize the
- vtable: they should call into the parent, and then overwrite th
- vtable if they need to.
+ Destruction of these objects is also done in the C-like way:
+ class_name_destroy (ClassName *object)
+
+ This is done so that our public API can be easily P/Invoked
+ from the managed side.
+
* Items
Items for the base class for all the objects that are rendered,
Modified: trunk/moon/src/Makefile.am
===================================================================
--- trunk/moon/src/Makefile.am 2007-05-26 16:56:17 UTC (rev 77990)
+++ trunk/moon/src/Makefile.am 2007-05-26 19:17:39 UTC (rev 77991)
@@ -3,12 +3,12 @@
lib_LTLIBRARIES = libmoon.la
libmoon_la_SOURCES = \
- runtime.c \
+ runtime.cpp \
runtime.h \
- video.c \
+ video.cpp \
video.h
demo_SOURCES = \
- demo.c
+ demo.cpp
demo_LDADD = -lmoon
\ No newline at end of file
Deleted: trunk/moon/src/demo.c
===================================================================
--- trunk/moon/src/demo.c 2007-05-26 16:56:17 UTC (rev 77990)
+++ trunk/moon/src/demo.c 2007-05-26 19:17:39 UTC (rev 77991)
@@ -1,102 +0,0 @@
-#define VIDEO_DEMO
-#include <string.h>
-#include <gtk/gtk.h>
-#include <malloc.h>
-#include <glib.h>
-#include <stdlib.h>
-
-#include "runtime.h"
-
-static Item *r, *v;
-
-gboolean
-expose_event_callback (GtkWidget *widget, GdkEventExpose *event, gpointer data)
-{
- Surface *t = (Surface *) data;
-
- if (event->area.x > t->width || event->area.y > t->height)
- return TRUE;
-
- printf ("Got a request to repaint at %d %d %d %d\n", event->area.x,
event->area.y, event->area.width, event->area.height);
- surface_repaint (t, event->area.x, event->area.y, event->area.width,
event->area.height);
- gdk_draw_pixbuf (widget->window,
- NULL,
- t->pixbuf,
- event->area.x, event->area.y, // gint src_x, gint
src_y,
- event->area.x, event->area.y, // gint dest_x, gint
dest_y,
- MIN (event->area.width, t->width),
- MIN (event->area.height, t->height),
- GDK_RGB_DITHER_NORMAL,
- 0, 0 // gint x_dither, gint y_dither
- );
- return TRUE;
-}
-
-static gboolean
-repaint (gpointer data)
-{
- static double degree = 0;
- static double degree2 = 0;
- double trans [6];
- double trans2 [6];
-
- cairo_matrix_init_rotate ((cairo_matrix_t *)&trans, degree);
- degree += 0.01;
-
- if (v != NULL)
- item_transform_set (v, (double *) trans);
-
- cairo_matrix_init_rotate ((cairo_matrix_t *)&trans2, degree2);
- degree2 -= 0.1;
- item_transform_set (r, (double *) trans2);
-
- return TRUE;
-}
-
-int
-main (int argc, char *argv [])
-{
- GtkWidget *w;
- GtkWidget *da;
- cairo_matrix_t trans;
-
- gtk_init (&argc, &argv);
- g_thread_init (NULL);
- gdk_threads_init ();
- runtime_init ();
-
- w = gtk_window_new (GTK_WINDOW_TOPLEVEL);
- da = gtk_drawing_area_new ();
- gtk_container_add (GTK_CONTAINER (w), da);
-
- // Create our objects
- Surface *t = surface_new (600, 600);
- t->data = w;
- r = rectangle_new (100, 100, 100, 100);
- cairo_matrix_init_rotate (&trans, 0.4);
- item_transform_set (r, (double *) (&trans));
- surface_repaint (t, 0, 0, 300, 300);
-
-#ifdef VIDEO_DEMO
- v = video_new ("file:///tmp/BoxerSmacksdownInhoffe.wmv", 0, 0);
- //printf ("Got %d\n", v);
- item_transform_set (v, (double *) (&trans));
- group_item_add ((Group *) t, v);
-#endif
-
- group_item_add ((Group *) t, r);
-
-#ifdef VIDEO_DEMO
- Item *v2 = video_new ("file:///tmp/Countdown-Colbert-BestNailings.wmv",
100, 100);
- group_item_add ((Group *) t, v2);
-#endif
-
- gtk_signal_connect (GTK_OBJECT (w), "expose_event",
- G_CALLBACK (expose_event_callback), t);
-
- gtk_widget_set_usize (w, 400, 400);
- gtk_widget_show (w);
- gtk_timeout_add (60, repaint, w);
- //g_idle_add (repaint, w);
- gtk_main ();
-}
Copied: trunk/moon/src/demo.cpp (from rev 77990, trunk/moon/src/demo.c)
===================================================================
--- trunk/moon/src/demo.c 2007-05-26 16:56:17 UTC (rev 77990)
+++ trunk/moon/src/demo.cpp 2007-05-26 19:17:39 UTC (rev 77991)
@@ -0,0 +1,101 @@
+#define VIDEO_DEMO
+#include <string.h>
+#include <gtk/gtk.h>
+#include <malloc.h>
+#include <glib.h>
+#include <stdlib.h>
+
+#include "runtime.h"
+
+static Item *r, *v;
+
+gboolean
+expose_event_callback (GtkWidget *widget, GdkEventExpose *event, gpointer data)
+{
+ Surface *t = (Surface *) data;
+
+ if (event->area.x > t->width || event->area.y > t->height)
+ return TRUE;
+
+ printf ("Got a request to repaint at %d %d %d %d\n", event->area.x,
event->area.y, event->area.width, event->area.height);
+ surface_repaint (t, event->area.x, event->area.y, event->area.width,
event->area.height);
+ gdk_draw_pixbuf (widget->window,
+ NULL,
+ t->pixbuf,
+ event->area.x, event->area.y, // gint src_x, gint
src_y,
+ event->area.x, event->area.y, // gint dest_x, gint
dest_y,
+ MIN (event->area.width, t->width),
+ MIN (event->area.height, t->height),
+ GDK_RGB_DITHER_NORMAL,
+ 0, 0 // gint x_dither, gint y_dither
+ );
+ return TRUE;
+}
+
+static gboolean
+repaint (gpointer data)
+{
+ static double degree = 0;
+ static double degree2 = 0;
+ double trans [6];
+ double trans2 [6];
+
+ cairo_matrix_init_rotate ((cairo_matrix_t *)&trans, degree);
+ degree += 0.01;
+
+ if (v != NULL)
+ item_transform_set (v, (double *) trans);
+
+ cairo_matrix_init_rotate ((cairo_matrix_t *)&trans2, degree2);
+ degree2 -= 0.1;
+ item_transform_set (r, (double *) trans2);
+
+ return TRUE;
+}
+
+int
+main (int argc, char *argv [])
+{
+ GtkWidget *w;
+ GtkWidget *da;
+ cairo_matrix_t trans;
+
+ gtk_init (&argc, &argv);
+ g_thread_init (NULL);
+ gdk_threads_init ();
+
+ w = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ da = gtk_drawing_area_new ();
+ gtk_container_add (GTK_CONTAINER (w), da);
+
+ // Create our objects
+ Surface *t = surface_new (600, 600);
+ t->data = w;
+ r = rectangle_new (100, 100, 100, 100);
+ cairo_matrix_init_rotate (&trans, 0.4);
+ item_transform_set (r, (double *) (&trans));
+ surface_repaint (t, 0, 0, 300, 300);
+
+#ifdef VIDEO_DEMO
+ v = video_new ("file:///tmp/BoxerSmacksdownInhoffe.wmv", 0, 0);
+ //printf ("Got %d\n", v);
+ item_transform_set (v, (double *) (&trans));
+ group_item_add ((Group *) t, v);
+#endif
+
+ group_item_add ((Group *) t, r);
+
+#ifdef VIDEO_DEMO
+ Item *v2 = video_new ("file:///tmp/Countdown-Colbert-BestNailings.wmv",
100, 100);
+ group_item_add ((Group *) t, v2);
+#endif
+
+ gtk_signal_connect (GTK_OBJECT (w), "expose_event",
+ G_CALLBACK (expose_event_callback), t);
+
+ gtk_widget_set_usize (w, 400, 400);
+ gtk_widget_show (w);
+ gtk_timeout_add (60, repaint, w);
+ //g_idle_add (repaint, w);
+ gtk_main ();
+}
Deleted: trunk/moon/src/runtime.c
===================================================================
--- trunk/moon/src/runtime.c 2007-05-26 16:56:17 UTC (rev 77990)
+++ trunk/moon/src/runtime.c 2007-05-26 19:17:39 UTC (rev 77991)
@@ -1,501 +0,0 @@
-/*
- * runtime.cpp: Core surface and canvas definitions.
- *
- * Author:
- * Miguel de Icaza ([EMAIL PROTECTED])
- *
- * Copyright 2007 Novell, Inc. (http://www.novell.com)
- *
- * See the LICENSE file included with the distribution for details.
- *
- */
-
-#include <string.h>
-#include <gtk/gtk.h>
-#include <malloc.h>
-#include <glib.h>
-#include <stdlib.h>
-#if AGG
-# include <agg_rendering_buffer.h>
-# include "Agg2D.h"
-#else
-# define CAIRO 1
-#endif
-
-#include "runtime.h"
-
-#if AGG
-struct _SurfacePrivate {
- agg::rendering_buffer rbuf;
- Agg2D *graphics;
-};
-#endif
-
-ObjectVtable object_vtable = { NULL } ;
-
-void
-object_init (Object *obj)
-{
- obj->vtable = &object_vtable;
-}
-
-void
-object_class_init ()
-{
- // Nothing.
-}
-
-static void
-vt_item_render (Item *item, Surface *s, double *affine, int x, int y, int
width, int height)
-{
- fprintf (stderr, "vt_item_render called, you must implement this
method");
-}
-
-static void
-vt_item_getbounds (Item *item)
-{
- fprintf (stderr, "vt_item_getbounds called, you must implement this
method");
-}
-
-ItemVtable item_vtable;
-
-item_class_init ()
-{
- item_vtable.object_vtable = object_vtable;
- item_vtable.render = vt_item_render;
- item_vtable.getbounds = vt_item_getbounds;
-}
-
-/**
- * item_getbounds:
- * @item: the item to update the bounds of
- *
- * Does this by requesting bounds update to all of its parents.
- */
-void
-item_update_bounds (Item *item)
-{
- double cx1 = item->x1;
- double cy1 = item->y1;
- double cx2 = item->x2;
- double cy2 = item->y2;
-
- ItemVtable *vt = (ItemVtable *) item->object.vtable;
- vt->getbounds (item);
-
- //
- // If we changed, notify the parent to recompute its bounds
- //
- if (item->x1 != cx1 || item->y1 != cy1 || item->y2 != cy2 || item->x2
!= cx2){
- if (item->parent != NULL)
- item_update_bounds (item->parent);
- }
-}
-
-void
-item_init (Item *item)
-{
- object_init (&item->object);
- item->object.vtable = &item_vtable;
-}
-
-/*
- * item_get_affine:
- * @container: the affine transform from the container
- * @affine: the affine transform for this item
- * @result: a place that can contain 6 doubles where the data is saved
- *
- * Composites the container affine transformation with the item affine
- * transformation.
- *
- * Returns the pointer to result if the caller must apply affine
- * transformations, or NULL if the affine should not be used.
- */
-double *
-item_get_affine (double *container, double *affine, double *result)
-{
- int i;
-
- if (container == NULL){
- if (affine == NULL)
- return FALSE;
- for (i = 0; i < 6; i++)
- result [i] = affine [i];
- return result;
- }
- if (affine == NULL){
- for (i = 0; i < 6; i++)
- result [i] = container [i];
- return result;
- }
-
- result [0] = container [0] * affine [0] + container [1] * affine [2];
- result [1] = container [0] * affine [1] + container [1] * affine [3];
- result [2] = container [2] * affine [0] + container [3] * affine [2];
- result [3] = container [2] * affine [1] + container [3] * affine [3];
- result [4] = container [4] * affine [0] + container [5] * affine [2] +
affine [4];
- result [5] = container [4] * affine [1] + container [5] * affine [3] +
affine [5];
-
- return result;
-}
-
-void
-item_destroy (Item *item)
-{
- if (item->xform)
- free (item->xform);
-}
-
-//
-// item_affine_get_absolute:
-//
-// Returns the absolute affine transformation for this given item
-// (composing the full chain from the top to this item)
-//
-double *
-item_affine_get_absolute (Item *item, double *result)
-{
- double res [6];
- double *ret;
-
- if (item->parent == NULL){
- return item_get_affine (NULL, item->xform, result);
- }
-
- ret = item_affine_get_absolute (item->parent, res);
- return item_get_affine (ret, item->xform, result);
-}
-
-void
-item_invalidate (Item *item)
-{
- double res [6];
- double *affine = item_affine_get_absolute (item, res);
- Surface *s = item_surface_get (item);
-
- if (s == NULL)
- return;
-
-#ifdef DEBUG_INVALIDATE
- printf ("Requesting invalidate for %d %d %d %d\n",
- (int) item->x1, (int)item->y1,
- (int)(item->x2-item->x1),
(int)(item->y2-item->y1));
-#endif
- gtk_widget_queue_draw_area ((GtkWidget *)s->data,
- (int) item->x1, (int)item->y1,
- (int)(item->x2-item->x1),
(int)(item->y2-item->y1));
-}
-
-void
-item_transform_set (Item *item, double *transform)
-{
- double *d;
-
- item_invalidate (item);
-
- if (item->xform)
- free (item->xform);
-
- if (transform == NULL)
- item->xform = transform;
- else {
- d = (double *) malloc (sizeof (double) * 6);
-
- d [0] = transform [0];
- d [1] = transform [1];
- d [2] = transform [2];
- d [3] = transform [3];
- d [4] = transform [4];
- d [5] = transform [5];
-
- item->xform = d;
- }
- ItemVtable *vt = (ItemVtable *) item->object.vtable;
- vt->getbounds (item);
- item_invalidate (item);
-}
-
-static void
-vt_group_render (Item *item, Surface *s, double *affine, int x, int y, int
width, int height)
-{
- Group *group = (Group *) item;
- GSList *il;
- double actual [6];
- double *use_affine = item_get_affine (affine, item->xform, actual);
-
- for (il = group->items; il != NULL; il = il->next){
- Item *item = (Item *) il->data;
- ItemVtable *vt = (ItemVtable *) item->object.vtable;
-
- vt->render (item, s, use_affine, x, y, width, height);
- }
-}
-
-static void
-vt_group_getbounds (Item *item)
-{
-
-}
-
-static ItemVtable group_vtable;
-
-static void
-group_class_init ()
-{
- group_vtable.object_vtable = object_vtable;
- group_vtable.render = vt_group_render;
- group_vtable.getbounds = vt_group_getbounds;
-}
-
-void
-group_init (Group *group)
-{
- item_init (&group->item);
- group->item.object.vtable = &group_vtable;
-}
-
-void
-group_item_add (Group *group, Item *item)
-{
- group->items = g_slist_append (group->items, item);
- item->parent = (Item *) group;
-
- ItemVtable *vt = (ItemVtable *) item->object.vtable;
- vt->getbounds (item);
-}
-
-void
-shape_class_init ()
-{
- // Nothing, this is pure abstract
-}
-
-void
-shape_init (Shape *shape)
-{
- item_init (&shape->item);
-}
-
-static void
-rectangle_draw (Item *item, Surface *s, double *affine)
-{
- Rectangle *r = (Rectangle *) item;
- double result [6];
- double *matrix = item_get_affine (affine, item->xform, result);
-
- cairo_save (s->cairo);
- if (matrix != NULL)
- cairo_set_matrix (s->cairo, (cairo_matrix_t *) matrix);
-
- cairo_rectangle (s->cairo, r->x, r->y, r->w, r->h);
- cairo_restore (s->cairo);
-}
-
-static ItemVtable surface_vtable;
-
-static void
-surface_class_init ()
-{
- surface_vtable = group_vtable;
-}
-
-Surface *
-item_surface_get (Item *item)
-{
- if (item->object.vtable == &surface_vtable)
- return (Surface *) item;
- if (item->parent != NULL)
- return item_surface_get (item->parent);
-
- return NULL;
-}
-
-static void
-vt_rectangle_render (Item *item, Surface *s, double *affine, int x, int y, int
width, int height)
-{
-#if CAIRO
- rectangle_draw (item, s, affine);
- cairo_stroke (s->cairo);
-#else
- Rectangle *r = (Rectangle *) item;
- Agg2D *g = s->priv->graphics;
-
- double result[6];
- double *matrix = item_get_affine (affine, item->xform, result);
-
- if (matrix != NULL){
- Agg2D::Affine a (matrix [0], matrix [1], matrix [2], matrix
[3], matrix [4], matrix [5]);
-
- g->affine (a);
- }
- g->lineColor (0, 0, 0);
- g->noFill ();
- g->rectangle (r->x, r->y, r->x + r->w, r->y + r->h);
- if (matrix != NULL)
- g->resetTransformations ();
-#endif
-}
-
-static void
-vt_rectangle_getbounds (Item *item)
-{
- double res [6];
- double *affine = item_affine_get_absolute (item, res);
-
- Surface *s = item_surface_get (item);
- if (s == NULL){
- // not yet attached
- return;
- }
-#if CAIRO
- rectangle_draw (item, s, affine);
- cairo_stroke_extents (s->cairo, &item->x1, &item->y1, &item->x2,
&item->y2);
- cairo_new_path (s->cairo);
-#else
-#endif
-}
-
-static ItemVtable rectangle_vtable;
-
-void
-rectangle_class_init ()
-{
- rectangle_vtable.object_vtable = object_vtable;
- rectangle_vtable.render = vt_rectangle_render;
- rectangle_vtable.getbounds = vt_rectangle_getbounds;
-}
-
-void
-rectangle_init (Rectangle *rectangle)
-{
- shape_init (&rectangle->shape);
- Item *i = &rectangle->shape.item;
- i->object.vtable = &rectangle_vtable;
-}
-
-Item *
-rectangle_new (double x, double y, double w, double h)
-{
- Rectangle *rect = g_new0 (Rectangle, 1);
- rectangle_init (rect);
-
- rect->x = x;
- rect->y = y;
- rect->w = w;
- rect->h = h;
-
- return (Item*)rect;
-}
-
-void
-surface_clear (Surface *s, int x, int y, int width, int height)
-{
- if (x < 0){
- width -= x;
- x = 0;
- }
-
- if (y < 0){
- height -= y;
- y = 0;
- }
- width = MAX (0, MIN (width, s->width));
- height = MAX (0, MIN (height, s->height));
-
- int stride = s->width * 4;
- unsigned char *dest = s->buffer + (y * stride) + (x * 4);
-
- while (height--){
- memset (dest, 14, width * 4);
- dest += stride;
- }
-}
-
-void
-surface_clear_all (Surface *s)
-{
- memset (s->buffer, 14, s->width * s->height * 4);
-}
-
-static void
-surface_realloc (Surface *s)
-{
- if (s->buffer){
- free (s->buffer);
- gdk_pixbuf_unref (s->pixbuf);
- }
-
- int size = s->width * s->height * 4;
- s->buffer = (unsigned char *) malloc (size);
- surface_clear_all (s);
-
- //
- // Hook up our surfaces
- //
-
- s->pixbuf = gdk_pixbuf_new_from_data (s->buffer, GDK_COLORSPACE_RGB,
TRUE, 8,
- s->width, s->height, s->width *
4, NULL, NULL);
-
-#if AGG
- s->priv = g_new0 (SurfacePrivate, 1);
- s->priv->rbuf = agg::rendering_buffer ();
- s->priv->rbuf.attach (s->buffer, s->width, s->height, s->width * 4);
- s->priv->graphics = new Agg2D();
- s->priv->graphics->attach (s->buffer, s->width, s->height, s->width *
4);
- s->priv->graphics->viewport (0, 0, s->width, s->height, 0, 0, s->width,
s->height, Agg2D::XMidYMid);
-#else
-
- s->cairo_surface = cairo_image_surface_create_for_data (s->buffer,
CAIRO_FORMAT_ARGB32, s->width, s->height, s->width * 4);
- s->cairo = cairo_create (s->cairo_surface);
- //cairo_set_operator (s->cairo, CAIRO_OPERATOR_SOURCE);
- //cairo_paint (s->cairo);
- //cairo_set_operator (s->cairo, CAIRO_OPERATOR_SOURCE);
- //cairo_set_source_rgb (s->cairo, 0, 1, 0);
-#endif
-}
-
-void
-surface_init (Surface *s, int width, int height)
-{
- group_init (&s->group);
- s->group.item.object.vtable = &surface_vtable;
- s->width = width;
- s->height = height;
- surface_realloc (s);
-}
-
-void
-surface_destroy (Surface *s)
-{
- cairo_surface_destroy (s->cairo_surface);
- free (s);
-}
-
-Surface *
-surface_new (int width, int height)
-{
- Surface *s = g_new0 (Surface, 1);
-
- surface_init (s, width, height);
-
- return s;
-}
-
-void
-surface_repaint (Surface *s, int x, int y, int width, int height)
-{
- surface_clear (s, x, y, width, height);
- vt_group_render ((Item *) s, s, NULL, x, y, width, height);
-}
-
-void
-runtime_init ()
-{
- object_class_init ();
- item_class_init ();
- group_class_init ();
- shape_class_init ();
- rectangle_class_init ();
- surface_class_init ();
- video_class_init ();
-}
Copied: trunk/moon/src/runtime.cpp (from rev 77981, trunk/moon/src/runtime.c)
===================================================================
--- trunk/moon/src/runtime.c 2007-05-25 23:05:50 UTC (rev 77981)
+++ trunk/moon/src/runtime.cpp 2007-05-26 19:17:39 UTC (rev 77991)
@@ -0,0 +1,376 @@
+/*
+ * runtime.cpp: Core surface and canvas definitions.
+ *
+ * Author:
+ * Miguel de Icaza ([EMAIL PROTECTED])
+ *
+ * Copyright 2007 Novell, Inc. (http://www.novell.com)
+ *
+ * See the LICENSE file included with the distribution for details.
+ *
+ */
+
+#include <string.h>
+#include <gtk/gtk.h>
+#include <malloc.h>
+#include <glib.h>
+#include <stdlib.h>
+#if AGG
+# include <agg_rendering_buffer.h>
+# include "Agg2D.h"
+#else
+# define CAIRO 1
+#endif
+
+#include "runtime.h"
+
+#if AGG
+struct _SurfacePrivate {
+ agg::rendering_buffer rbuf;
+ Agg2D *graphics;
+};
+#endif
+
+/**
+ * item_getbounds:
+ * @item: the item to update the bounds of
+ *
+ * Does this by requesting bounds update to all of its parents.
+ */
+void
+item_update_bounds (Item *item)
+{
+ double cx1 = item->x1;
+ double cy1 = item->y1;
+ double cx2 = item->x2;
+ double cy2 = item->y2;
+
+ item->getbounds ();
+
+ //
+ // If we changed, notify the parent to recompute its bounds
+ //
+ if (item->x1 != cx1 || item->y1 != cy1 || item->y2 != cy2 || item->x2
!= cx2){
+ if (item->parent != NULL)
+ item_update_bounds (item->parent);
+ }
+}
+
+/*
+ * item_get_affine:
+ * @container: the affine transform from the container
+ * @affine: the affine transform for this item
+ * @result: a place that can contain 6 doubles where the data is saved
+ *
+ * Composites the container affine transformation with the item affine
+ * transformation.
+ *
+ * Returns the pointer to result if the caller must apply affine
+ * transformations, or NULL if the affine should not be used.
+ */
+double *
+item_get_affine (double *container, double *affine, double *result)
+{
+ int i;
+
+ if (container == NULL){
+ if (affine == NULL)
+ return FALSE;
+ for (i = 0; i < 6; i++)
+ result [i] = affine [i];
+ return result;
+ }
+ if (affine == NULL){
+ for (i = 0; i < 6; i++)
+ result [i] = container [i];
+ return result;
+ }
+
+ result [0] = container [0] * affine [0] + container [1] * affine [2];
+ result [1] = container [0] * affine [1] + container [1] * affine [3];
+ result [2] = container [2] * affine [0] + container [3] * affine [2];
+ result [3] = container [2] * affine [1] + container [3] * affine [3];
+ result [4] = container [4] * affine [0] + container [5] * affine [2] +
affine [4];
+ result [5] = container [4] * affine [1] + container [5] * affine [3] +
affine [5];
+
+ return result;
+}
+
+void
+item_destroy (Item *item)
+{
+ if (item->xform)
+ free (item->xform);
+}
+
+//
+// item_affine_get_absolute:
+//
+// Returns the absolute affine transformation for this given item
+// (composing the full chain from the top to this item)
+//
+double *
+item_affine_get_absolute (Item *item, double *result)
+{
+ double res [6];
+ double *ret;
+
+ if (item->parent == NULL){
+ return item_get_affine (NULL, item->xform, result);
+ }
+
+ ret = item_affine_get_absolute (item->parent, res);
+ return item_get_affine (ret, item->xform, result);
+}
+
+void
+item_invalidate (Item *item)
+{
+ double res [6];
+ double *affine = item_affine_get_absolute (item, res);
+ Surface *s = item_surface_get (item);
+
+ if (s == NULL)
+ return;
+
+#ifdef DEBUG_INVALIDATE
+ printf ("Requesting invalidate for %d %d %d %d\n",
+ (int) item->x1, (int)item->y1,
+ (int)(item->x2-item->x1),
(int)(item->y2-item->y1));
+#endif
+ gtk_widget_queue_draw_area ((GtkWidget *)s->data,
+ (int) item->x1, (int)item->y1,
+ (int)(item->x2-item->x1),
(int)(item->y2-item->y1));
+}
+
+void
+item_transform_set (Item *item, double *transform)
+{
+ double *d;
+
+ item_invalidate (item);
+
+ if (item->xform)
+ free (item->xform);
+
+ if (transform == NULL)
+ item->xform = transform;
+ else {
+ d = (double *) malloc (sizeof (double) * 6);
+
+ d [0] = transform [0];
+ d [1] = transform [1];
+ d [2] = transform [2];
+ d [3] = transform [3];
+ d [4] = transform [4];
+ d [5] = transform [5];
+
+ item->xform = d;
+ }
+ item->getbounds ();
+ item_invalidate (item);
+}
+
+void
+Group::render (Surface *s, double *affine, int x, int y, int width, int height)
+{
+ GSList *il;
+ double actual [6];
+ double *use_affine = item_get_affine (affine, xform, actual);
+
+ for (il = items; il != NULL; il = il->next){
+ Item *item = (Item *) il->data;
+
+ item->render (s, use_affine, x, y, width, height);
+ }
+}
+
+void
+Group::getbounds ()
+{
+
+}
+
+void
+group_item_add (Group *group, Item *item)
+{
+ group->items = g_slist_append (group->items, item);
+ item->parent = (Item *) group;
+
+ item->getbounds ();
+}
+
+static void
+rectangle_draw (Rectangle *r, Surface *s, double *affine)
+{
+ double result [6];
+ double *matrix = item_get_affine (affine, r->xform, result);
+
+ cairo_save (s->cairo);
+ if (matrix != NULL)
+ cairo_set_matrix (s->cairo, (cairo_matrix_t *) matrix);
+
+ cairo_rectangle (s->cairo, r->x, r->y, r->w, r->h);
+ cairo_restore (s->cairo);
+}
+
+Surface *
+item_surface_get (Item *item)
+{
+ if (item->flags & Video::IS_SURFACE)
+ return (Surface *) item;
+
+ if (item->parent != NULL)
+ return item_surface_get (item->parent);
+
+ return NULL;
+}
+
+void
+Rectangle::render (Surface *s, double *affine, int x, int y, int width, int
height)
+{
+#if CAIRO
+ rectangle_draw (this, s, affine);
+ cairo_stroke (s->cairo);
+#else
+ Rectangle *r = (Rectangle *) item;
+ Agg2D *g = s->priv->graphics;
+
+ double result[6];
+ double *matrix = item_get_affine (affine, item->xform, result);
+
+ if (matrix != NULL){
+ Agg2D::Affine a (matrix [0], matrix [1], matrix [2], matrix
[3], matrix [4], matrix [5]);
+
+ g->affine (a);
+ }
+ g->lineColor (0, 0, 0);
+ g->noFill ();
+ g->rectangle (r->x, r->y, r->x + r->w, r->y + r->h);
+ if (matrix != NULL)
+ g->resetTransformations ();
+#endif
+}
+
+void
+Rectangle::getbounds ()
+{
+ double res [6];
+ double *affine = item_affine_get_absolute (this, res);
+
+ Surface *s = item_surface_get (this);
+ if (s == NULL){
+ // not yet attached
+ return;
+ }
+#if CAIRO
+ rectangle_draw (this, s, affine);
+ cairo_stroke_extents (s->cairo, &x1, &y1, &x2, &y2);
+ cairo_new_path (s->cairo);
+#else
+#endif
+}
+
+Rectangle *
+rectangle_new (double x, double y, double w, double h)
+{
+ Rectangle *rect = new Rectangle (x, y, w, h);
+
+ return rect;
+}
+
+void
+surface_clear (Surface *s, int x, int y, int width, int height)
+{
+ if (x < 0){
+ width -= x;
+ x = 0;
+ }
+
+ if (y < 0){
+ height -= y;
+ y = 0;
+ }
+ width = MAX (0, MIN (width, s->width));
+ height = MAX (0, MIN (height, s->height));
+
+ int stride = s->width * 4;
+ unsigned char *dest = s->buffer + (y * stride) + (x * 4);
+
+ while (height--){
+ memset (dest, 14, width * 4);
+ dest += stride;
+ }
+}
+
+void
+surface_clear_all (Surface *s)
+{
+ memset (s->buffer, 14, s->width * s->height * 4);
+}
+
+static void
+surface_realloc (Surface *s)
+{
+ if (s->buffer){
+ free (s->buffer);
+ gdk_pixbuf_unref (s->pixbuf);
+ }
+
+ int size = s->width * s->height * 4;
+ s->buffer = (unsigned char *) malloc (size);
+ surface_clear_all (s);
+
+ //
+ // Hook up our surfaces
+ //
+
+ s->pixbuf = gdk_pixbuf_new_from_data (s->buffer, GDK_COLORSPACE_RGB,
TRUE, 8,
+ s->width, s->height, s->width *
4, NULL, NULL);
+
+#if AGG
+ s->priv = g_new0 (SurfacePrivate, 1);
+ s->priv->rbuf = agg::rendering_buffer ();
+ s->priv->rbuf.attach (s->buffer, s->width, s->height, s->width * 4);
+ s->priv->graphics = new Agg2D();
+ s->priv->graphics->attach (s->buffer, s->width, s->height, s->width *
4);
+ s->priv->graphics->viewport (0, 0, s->width, s->height, 0, 0, s->width,
s->height, Agg2D::XMidYMid);
+#else
+
+ s->cairo_surface = cairo_image_surface_create_for_data (s->buffer,
CAIRO_FORMAT_ARGB32, s->width, s->height, s->width * 4);
+ s->cairo = cairo_create (s->cairo_surface);
+ //cairo_set_operator (s->cairo, CAIRO_OPERATOR_SOURCE);
+ //cairo_paint (s->cairo);
+ //cairo_set_operator (s->cairo, CAIRO_OPERATOR_SOURCE);
+ //cairo_set_source_rgb (s->cairo, 0, 1, 0);
+#endif
+}
+
+void
+surface_destroy (Surface *s)
+{
+ cairo_surface_destroy (s->cairo_surface);
+ delete s;
+}
+
+Surface *
+surface_new (int width, int height)
+{
+ Surface *s = new Surface ();
+
+ s->buffer = NULL;
+ s->flags |= Item::IS_SURFACE;
+ s->width = width;
+ s->height = height;
+ surface_realloc (s);
+
+ return s;
+}
+
+void
+surface_repaint (Surface *s, int x, int y, int width, int height)
+{
+ surface_clear (s, x, y, width, height);
+ s->render (s, NULL, x, y, width, height);
+}
+
Modified: trunk/moon/src/runtime.h
===================================================================
--- trunk/moon/src/runtime.h 2007-05-26 16:56:17 UTC (rev 77990)
+++ trunk/moon/src/runtime.h 2007-05-26 19:17:39 UTC (rev 77991)
@@ -4,77 +4,53 @@
#include <cairo.h>
-typedef struct _Item Item;
-typedef struct _Surface Surface;
-
-typedef struct {
- void (*nothing) ();
-} ObjectVtable;
+class Surface;
typedef struct {
- void *vtable;
-} Object;
-
-void object_init (Object *object);
-
-typedef struct {
double opacity;
double *relative_transform;
double *transform;
GList *listeners;
} Brush;
+
+class Item {
+ public:
+ Item () : parent(NULL), flags (0), xform (NULL), x1 (0), y1(0), x2(0),
y2(0) {}
-typedef struct {
- ObjectVtable object_vtable;
+ Item *parent;
+
+ enum ItemFlags {
+ IS_SURFACE = 1
+ };
+ int flags;
+
+ // The computed bounding box
+ double x1, y1, x2, y2;
+
+ // If null, identity, otherwise affine transformation.
+ double *xform;
+
//
// render:
// Renders the given @item on the @surface. The parent affine
transformation is in
// @affine, and the area that is exposed is delimited by x, y, width,
height
//
- void (*render) (Item *item, Surface *surface, double *affine, int x,
int y, int width, int height);
+ virtual void render (Surface *surface, double *affine, int x, int y,
int width, int height) = 0;
//
// getbounds:
// Updates the bounding box for the given item, this uses the parent
// chain to compute the composite affine.
//
- // Input parameters:
- // @item: the item
- //
// Output:
// the item->x1,y1,x2,y2 values are updated.
//
- void (*getbounds) (Item *item);
-} ItemVtable;
-
-struct _Item {
- Object object;
-
- // OpacityProperty;
- // ClipProperty;
- // RenderTransformProperty;
- // TriggersProperty;
- // OpacityMaskProperty;
- // RenderTransformOriginProperty;
- // CursorProperty;
- // IsHitTestVisibleProperty;
- // VisibilityProperty;
- // ResourcesProperty;
- // ZIndexProperty;
-
- Item *parent;
-
- // The computed bounding box
- double x1, y1, x2, y2;
-
- // If null, identity, otherwise affine transformation.
- double *xform;
+ virtual void getbounds () = 0;
};
Surface *item_surface_get (Item *item);
-void item_init (Item *item);
void item_destroy (Item *item);
void item_invalidate (Item *item);
void item_update_bounds (Item *item);
@@ -83,43 +59,58 @@
Surface *item_surface_get (Item *item);
double *item_affine_get_absolute (Item *item, double *result);
-typedef struct {
- Item item;
-
+class Group : public Item {
+ public:
GSList *items;
-} Group;
-void group_init (Group *group);
+ Group () : items (NULL) {}
+
+ virtual void render (Surface *s, double *affine, int x, int y, int
width, int height);
+ virtual void getbounds ();
+};
+
void group_item_add (Group *group, Item *item);
-typedef struct {
- Item item;
-
+class Shape : public Item {
// Brush: Fill, Stroke
-} Shape;
+};
-void shape_init (Shape *shape);
-
-typedef struct {
- Shape shape;
+class Rectangle : public Shape {
+ public:
double x, y, w, h;
-} Rectangle;
-void rectangle_init (Rectangle *rectangle);
-Item *rectangle_new (double x, double y, double w, double h);
+ Rectangle (double ix, double iy, double iw, double ih) : x(ix), y(iy),
w(iw), h(ih) {};
+ Rectangle () : x(0), y(0), w(0), h(0) {};
+
+ virtual void render (Surface *s, double *affine, int x, int y, int
width, int height);
+ virtual void getbounds ();
+};
+Rectangle *rectangle_new (double x, double y, double w, double h);
+
+typedef struct _VideoPrivate VideoPrivate;
+
// A video is an Item
-typedef struct _Video Video;
+class Video : public Item {
+ enum { VIDEO_OK, VIDEO_ERROR_OPEN, VIDEO_ERROR_STREAM_INFO } VideoError;
+ public:
+ double x, y;
+ char *filename;
+ int error;
-void video_init (Video *video);
-Item *video_new (const char *filename, double x, double y);
-void video_destroy (Video *video);
+ Video (const char *filename, double x, double y);
+ ~Video ();
+};
+Video *video_new (const char *filename, double x, double y);
+void video_destroy (Video *video);
+
typedef struct _SurfacePrivate SurfacePrivate;
-struct _Surface {
- Group group;
-
+class Surface : public Group {
+ public:
+ Surface () : width (0), height (0), buffer (0), pixbuf (NULL),
cairo_surface (NULL), cairo (NULL), data(NULL) {}
+
int width, height;
// The data lives here
@@ -149,12 +140,6 @@
void surface_destroy (Surface *s);
void surface_repaint (Surface *s, int x, int y, int width, int height);
-// Vtables
-extern ItemVtable item_vtable;
-
-// External class init
-void video_class_init ();
-
#ifdef __cplusplus
};
#endif
Deleted: trunk/moon/src/video.c
===================================================================
--- trunk/moon/src/video.c 2007-05-26 16:56:17 UTC (rev 77990)
+++ trunk/moon/src/video.c 2007-05-26 19:17:39 UTC (rev 77991)
@@ -1,406 +0,0 @@
-/*
- * video.c: The moonlight's video item
- *
- * Author:
- * Miguel de Icaza ([EMAIL PROTECTED])
- *
- * Copyright 2007 Novell, Inc. (http://www.novell.com)
- *
- * See the LICENSE file included with the distribution for details.
- *
- */
-#include <string.h>
-#include <gtk/gtk.h>
-#include <cairo.h>
-#include <malloc.h>
-#include <glib.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <errno.h>
-#include "runtime.h"
-
-//
-#include <fcntl.h>
-
-// video specific
-#include <stdint.h>
-#include <avcodec.h>
-#include <avformat.h>
-#include <swscale.h>
-
-typedef enum {
- VIDEO_OK,
- VIDEO_ERROR_OPEN,
- VIDEO_ERROR_STREAM_INFO
-} VideoError;
-
-typedef enum {
- // When a new frame is ready
- CMD_NEWFRAME,
-
- // When the video has been loaded, this updates the bounding box in the
Gtk+ GUI thread.
- CMD_INITED
-} GuiCommand;
-
-struct _Video {
- Item item;
- double x, y, w, h;
- char *filename;
-
- int error;
-
- GThread *decode_thread_id;
- int pipe [2];
-
- //
- // AV Fields
- //
- AVFormatContext *av_format_context;
-
- // Video
- AVCodec *video_codec;
- AVStream *video_stream;
- int video_stream_idx;
- GMutex *video_mutex;
-
- GAsyncQueue *video_frames;
-
- //
- // The components used to render the buffer
- //
-
- // The buffer where we store the RGB data
- void *video_rgb_buffer;
-
- // The cairo surface that represents video_rgb_buffer
- cairo_surface_t *video_cairo_surface;
-
- // The SwsContext image convertor
- struct SwsContext *video_scale_context;
-
-};
-
-static GMutex *video_lock;
-
-static void
-vt_video_getbounds (Item *item)
-{
- Video *video = (Video *) item;
- double res [6];
- double *affine = item_affine_get_absolute (item, res);
- AVCodecContext *cc;
-
- Surface *s = item_surface_get (item);
- if (s == NULL){
- // not yet attached
- return;
- }
-
- // If we have not been initialized yet, we cant compute the bounds
- if (video->video_stream == NULL || video->video_stream->codec == NULL){
- item->x1 = item->y1 = item->x2 = item->y2 = 0;
- return;
- }
- cc = video->video_stream->codec;
-
- cairo_save (s->cairo);
- if (affine != NULL)
- cairo_set_matrix (s->cairo, (cairo_matrix_t *) affine);
-
- cairo_rectangle (s->cairo, video->x, video->y, video->x + cc->width,
video->y + cc->height);
- cairo_fill_extents (s->cairo, &item->x1, &item->y1, &item->x2,
&item->y2);
-
- printf ("New video bounds: %g %g %g %g\n", item->x1, item->y1,
item->x2, item->y2);
- cairo_restore (s->cairo);
-}
-
-static void vt_video_render (Item *item, Surface *s, double *affine, int x,
int y, int width, int height);
-static int video_inited;
-
-ItemVtable video_vtable;
-
-void
-video_init (Video *video)
-{
- Item *item = &video->item;
- item_init (item);
- item->object.vtable = &video_vtable;
-
- if (!video_inited){
- video_lock = g_mutex_new ();
- avcodec_init ();
- av_register_all ();
- avcodec_register_all ();
- video_inited = TRUE;
- }
-}
-
-static void
-send_command (Video *video, uint8_t cmd)
-{
- int ret;
-
- do {
- ret = write (video->pipe [1], &cmd, 1);
- } while (ret == -1 && errno == EINTR);
-}
-
-//
-// The decoder thread, runs until complete
-//
-static gpointer
-decoder (gpointer data)
-{
- Video *video = data;
- AVFrame *video_frame;
- int i;
- int video_stream_idx;
-
- printf ("Video THREAD STARTS\n");
-
- g_mutex_lock (video_lock);
- // avcodec setup
- // auto-detect format
- // default buffer size
- // no additional parameters
- if (av_open_input_file (&video->av_format_context, video->filename,
NULL, 0, NULL) < 0){
- g_error ("ERROR: open input file\n");
- }
- if (video->av_format_context == (void *) &rtsp_demuxer){
- g_error ("ERROR: stream info\n");
- }
-
- av_read_play (video->av_format_context);
-
- if (av_find_stream_info(video->av_format_context) < 0){
- g_error ("ERROR: stream info\n");
- }
-
- for (i = 0; i < video->av_format_context->nb_streams; i++){
- AVStream *stream = video->av_format_context->streams [i];
- AVCodecContext *cc;
-
- switch (stream->codec->codec_type){
- case CODEC_TYPE_VIDEO:
- video->video_codec = avcodec_find_decoder
(stream->codec->codec_id);
- video->video_stream = video->av_format_context->streams
[i];
- avcodec_open (video->video_stream->codec,
video->video_codec);
- video->video_stream_idx = i;
- video_stream_idx = i;
-
- //
- // Allocate our rendering buffer and cairo surface
- //
- cc = video->video_stream->codec;
- video->video_rgb_buffer = malloc (cc->width *
cc->height * 4);
-
- video->video_cairo_surface =
cairo_image_surface_create_for_data (
- video->video_rgb_buffer, CAIRO_FORMAT_ARGB32,
- cc->width, cc->height, cc->width * 4);
-
- // The YUB to RGB scaleter/translator
- video->video_scale_context = sws_getContext (cc->width,
cc->height, cc->pix_fmt, cc->width, cc->height,
-
PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
- break;
- }
- }
- g_mutex_unlock (video_lock);
-
- send_command (video, CMD_INITED);
-
- while (TRUE){
- AVPacket packet, *pkt = &packet;
-
- int ret;
-
- ret = av_read_frame (video->av_format_context, pkt);
- if (ret == -1){
- printf ("Failed to read frame, terminating\n");
- break;
- }
-
- if (pkt->stream_index == video_stream_idx){
- int got_picture, n;
-
- video_frame = avcodec_alloc_frame ();
-
- while (g_async_queue_length (video->video_frames) > 10)
- g_usleep (1000);
-
- printf ("pkt=%ld\r", pkt->pts);
- avcodec_decode_video (video->video_stream->codec,
video_frame, &got_picture, pkt->data, pkt->size);
-
- if (got_picture){
- g_mutex_lock (video->video_mutex);
- send_command (video, CMD_NEWFRAME);
- g_async_queue_push (video->video_frames,
video_frame);
- g_mutex_unlock (video->video_mutex);
- }
- }
- av_free_packet (pkt);
- }
-
- printf ("THREAD COMPLETES\n");
-
- return NULL;
-}
-
-//
-// Convert the AVframe into an RGB buffer we can render
-//
-void
-convert_to_rgb (Video *video, AVFrame *frame)
-{
- static struct SwsContext *img_convert_ctx;
- AVCodecContext *cc = video->video_stream->codec;
- AVPicture pict;
- uint8_t *rgb_dest[3] = { video->video_rgb_buffer, NULL, NULL};
- int rgb_stride [3] = { cc->width * 4, 0, 0 };
-
- sws_scale (video->video_scale_context, frame->data, frame->linesize, 0,
- video->video_stream->codec->height, rgb_dest, rgb_stride);
-}
-
-//
-// Called on the GUI thread when we got a frame to render.
-//
-gboolean
-video_ready (GIOChannel *source, GIOCondition condition, gpointer data)
-{
- Video *video = (Video *) data;
- AVFrame *frame;
- int8_t commands [32];
- int ret, i;
-
- do {
- ret = read (video->pipe [0], &commands, sizeof (commands));
- } while ((ret == -1 && ret == EINTR));
-
- // Nothing.
- if (ret <= 0)
- return TRUE;
-
- for (i = 0; i < ret; i++){
- switch (commands [i]){
- case CMD_NEWFRAME:
- item_invalidate ((Item *) video);
- break;
-
- case CMD_INITED:
- item_update_bounds ((Item *) video);
- break;
-
- default:
- fprintf (stderr, "video_ready: unknown command from
decoding thread (%d)\n", commands [i]);
- }
- }
-
- return TRUE;
-}
-
-static void
-update_frame (Video *video)
-{
- AVFrame *frame = NULL;
-
- //
- // Experimental strategy: the strategy needs to be entirely redone
- // for now I just pull the last frame and discard all the other
- // intermediate frames that we already decoded.
- //
- //
- g_mutex_lock (video->video_mutex);
- while (g_async_queue_length (video->video_frames) != 0){
- void *rgb;
-
- if (frame != NULL)
- av_free (frame);
-
- frame = (AVFrame *) g_async_queue_pop (video->video_frames);
- }
- g_mutex_unlock (video->video_mutex);
-
- if (frame != NULL){
- convert_to_rgb (video, frame);
- av_free (frame);
- }
-}
-
-static void
-vt_video_render (Item *item, Surface *s, double *affine, int x, int y, int
width, int height)
-{
- Video *video = (Video *) item;
- double actual [6];
- double *use_affine = item_get_affine (affine, item->xform, actual);
- cairo_pattern_t *pattern, *old_pattern;
-
- update_frame (video);
-
- cairo_save (s->cairo);
- if (use_affine != NULL)
- cairo_set_matrix (s->cairo, (cairo_matrix_t *) use_affine);
-
- cairo_translate (s->cairo, video->x, video->y);
- cairo_set_source_surface (s->cairo, video->video_cairo_surface, 0, 0);
- cairo_paint (s->cairo);
-
- cairo_rectangle (s->cairo, video->x, video->y, video->w, video->h);
-
- cairo_restore (s->cairo);
-}
-
-/*
- * video_new:
- * @filename:
- * @x, @y: Position where to position the video
- *
- */
-Item *
-video_new (const char *filename, double x, double y)
-{
- Video *video = g_new0 (Video, 1);
- AVFormatParameters *ap;
-
- video_init (video);
-
- video->filename = g_strdup (filename);
- video->x = x;
- video->y = y;
- video->video_frames = g_async_queue_new ();
-
- //
- // Prepare separate thread
- //
- video->video_mutex = g_mutex_new ();
- pipe (video->pipe);
- fcntl (video->pipe [0], F_SETFL, O_NONBLOCK);
-
- g_io_add_watch (g_io_channel_unix_new (video->pipe [0]),
- G_IO_IN,
- video_ready,
- video);
-
- // Start decoding thread
- video->decode_thread_id = g_thread_create (decoder, video, TRUE, NULL);
-
- return (Item *) video;
-}
-
-void
-video_destroy (Video *video)
-{
- g_free (video->video_rgb_buffer);
- cairo_surface_destroy (video->video_cairo_surface);
-
- g_free (video->filename);
- g_free (video);
-}
-
-void
-video_class_init ()
-{
- video_vtable = item_vtable;
- video_vtable.render = &vt_video_render;
- video_vtable.getbounds = &vt_video_getbounds;
-}
-
Copied: trunk/moon/src/video.cpp (from rev 77981, trunk/moon/src/video.c)
===================================================================
--- trunk/moon/src/video.c 2007-05-25 23:05:50 UTC (rev 77981)
+++ trunk/moon/src/video.cpp 2007-05-26 19:17:39 UTC (rev 77991)
@@ -0,0 +1,417 @@
+/*
+ * video.c: The moonlight's video item
+ *
+ * Author:
+ * Miguel de Icaza ([EMAIL PROTECTED])
+ *
+ * Copyright 2007 Novell, Inc. (http://www.novell.com)
+ *
+ * See the LICENSE file included with the distribution for details.
+ *
+ */
+#define __STDC_CONSTANT_MACROS
+#include <unistd.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <cairo.h>
+#include <malloc.h>
+#include <glib.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include "runtime.h"
+
+//
+#include <fcntl.h>
+
+// video specific
+#include <avformat.h>
+#include <stdint.h>
+#include <avcodec.h>
+#include <swscale.h>
+
+Video::Video (const char *filename, double x, double y)
+{
+ this->filename = g_strdup (filename);
+ this->x = x;
+ this->y = y;
+}
+
+Video::~Video ()
+{
+ g_free (filename);
+}
+
+typedef enum {
+ // When a new frame is ready
+ CMD_NEWFRAME,
+
+ // When the video has been loaded, this updates the bounding box in the
Gtk+ GUI thread.
+ CMD_INITED
+} GuiCommand;
+
+class VideoFfmpeg : public Video {
+public:
+ int w, h;
+
+ VideoFfmpeg (const char *filename, double x, double y);
+ ~VideoFfmpeg ();
+
+ GThread *decode_thread_id;
+ int pipes [2];
+ GIOChannel *pipe_channel;
+
+ //
+ // AV Fields
+ //
+ AVFormatContext *av_format_context;
+
+ // Video
+ AVCodec *video_codec;
+ AVStream *video_stream;
+ int video_stream_idx;
+ GMutex *video_mutex;
+
+ GAsyncQueue *video_frames;
+
+ //
+ // The components used to render the buffer
+ //
+
+ // The buffer where we store the RGB data
+ unsigned char *video_rgb_buffer;
+
+ // The cairo surface that represents video_rgb_buffer
+ cairo_surface_t *video_cairo_surface;
+
+ // The SwsContext image convertor
+ struct SwsContext *video_scale_context;
+
+ // Virtual method overrides
+ void getbounds ();
+ void render (Surface *s, double *affine, int x, int y, int width, int
height);
+
+};
+
+static GMutex *video_lock;
+
+void
+VideoFfmpeg::getbounds ()
+{
+ double res [6];
+ double *affine = item_affine_get_absolute (this, res);
+ AVCodecContext *cc;
+
+ Surface *s = item_surface_get (this);
+ if (s == NULL){
+ // not yet attached
+ return;
+ }
+
+ // If we have not been initialized yet, we cant compute the bounds
+ if (video_stream == NULL || video_stream->codec == NULL){
+ x1 = y1 = x2 = y2 = 0;
+ return;
+ }
+ cc = video_stream->codec;
+
+ cairo_save (s->cairo);
+ if (affine != NULL)
+ cairo_set_matrix (s->cairo, (cairo_matrix_t *) affine);
+
+ cairo_rectangle (s->cairo, x, y, x + cc->width, y + cc->height);
+ cairo_fill_extents (s->cairo, &x1, &y1, &x2, &y2);
+
+ printf ("New video bounds: %g %g %g %g\n", x1, y1, x2, y2);
+ cairo_restore (s->cairo);
+}
+
+static void
+send_command (VideoFfmpeg *video, uint8_t cmd)
+{
+ int ret;
+
+ do {
+ ret = write (video->pipes [1], &cmd, 1);
+ } while (ret == -1 && errno == EINTR);
+}
+
+//
+// The decoder thread, runs until complete
+//
+static gpointer
+decoder (gpointer data)
+{
+ VideoFfmpeg *video = (VideoFfmpeg *) data;
+ AVFrame *video_frame;
+ int i;
+ int video_stream_idx;
+
+ printf ("Video THREAD STARTS\n");
+
+ g_mutex_lock (video_lock);
+ // avcodec setup
+ // auto-detect format
+ // default buffer size
+ // no additional parameters
+ if (av_open_input_file (&video->av_format_context, video->filename,
NULL, 0, NULL) < 0){
+ g_error ("ERROR: open input file\n");
+ }
+ if (video->av_format_context == (void *) &rtsp_demuxer){
+ g_error ("ERROR: stream info\n");
+ }
+
+ av_read_play (video->av_format_context);
+
+ if (av_find_stream_info(video->av_format_context) < 0){
+ g_error ("ERROR: stream info\n");
+ }
+
+ for (i = 0; i < video->av_format_context->nb_streams; i++){
+ AVStream *stream = video->av_format_context->streams [i];
+ AVCodecContext *cc;
+
+ switch (stream->codec->codec_type){
+ case CODEC_TYPE_VIDEO:
+ video->video_codec = avcodec_find_decoder
(stream->codec->codec_id);
+ video->video_stream = video->av_format_context->streams
[i];
+ avcodec_open (video->video_stream->codec,
video->video_codec);
+ video->video_stream_idx = i;
+ video_stream_idx = i;
+
+ //
+ // Allocate our rendering buffer and cairo surface
+ //
+ cc = video->video_stream->codec;
+ video->video_rgb_buffer = (unsigned char *) malloc
(cc->width * cc->height * 4);
+
+ video->video_cairo_surface =
cairo_image_surface_create_for_data (
+ video->video_rgb_buffer, CAIRO_FORMAT_ARGB32,
+ cc->width, cc->height, cc->width * 4);
+
+ // The YUB to RGB scaleter/translator
+ video->video_scale_context = sws_getContext (cc->width,
cc->height, cc->pix_fmt, cc->width, cc->height,
+
PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
+ break;
+ }
+ }
+ g_mutex_unlock (video_lock);
+
+ send_command (video, CMD_INITED);
+
+ while (TRUE){
+ AVPacket packet, *pkt = &packet;
+
+ int ret;
+
+ ret = av_read_frame (video->av_format_context, pkt);
+ if (ret == -1){
+ printf ("Failed to read frame, terminating\n");
+ break;
+ }
+
+ if (pkt->stream_index == video_stream_idx){
+ int got_picture, n;
+
+ video_frame = avcodec_alloc_frame ();
+
+ while (g_async_queue_length (video->video_frames) > 10)
+ g_usleep (1000);
+
+ printf ("pkt=%ld\r", pkt->pts);
+ avcodec_decode_video (video->video_stream->codec,
video_frame, &got_picture, pkt->data, pkt->size);
+
+ if (got_picture){
+ g_mutex_lock (video->video_mutex);
+ send_command (video, CMD_NEWFRAME);
+ g_async_queue_push (video->video_frames,
video_frame);
+ g_mutex_unlock (video->video_mutex);
+ }
+ }
+ av_free_packet (pkt);
+ }
+
+ printf ("THREAD COMPLETES\n");
+
+ return NULL;
+}
+
+//
+// Convert the AVframe into an RGB buffer we can render
+//
+void
+convert_to_rgb (VideoFfmpeg *video, AVFrame *frame)
+{
+ static struct SwsContext *img_convert_ctx;
+ AVCodecContext *cc = video->video_stream->codec;
+ AVPicture pict;
+ uint8_t *rgb_dest[3] = { video->video_rgb_buffer, NULL, NULL};
+ int rgb_stride [3] = { cc->width * 4, 0, 0 };
+
+ sws_scale (video->video_scale_context, frame->data, frame->linesize, 0,
+ video->video_stream->codec->height, rgb_dest, rgb_stride);
+}
+
+//
+// Called on the GUI thread when we got a frame to render.
+//
+gboolean
+video_ready (GIOChannel *source, GIOCondition condition, gpointer data)
+{
+ VideoFfmpeg *video = (VideoFfmpeg *) data;
+ AVFrame *frame;
+ int8_t commands [32];
+ int ret, i;
+
+ do {
+ ret = read (video->pipes [0], &commands, sizeof (commands));
+ } while ((ret == -1 && ret == EINTR));
+
+ // Nothing.
+ if (ret <= 0)
+ return TRUE;
+
+ for (i = 0; i < ret; i++){
+ switch (commands [i]){
+ case CMD_NEWFRAME:
+ item_invalidate ((Item *) video);
+ break;
+
+ case CMD_INITED:
+ item_update_bounds ((Item *) video);
+ video->w = video->video_stream->codec->width;
+ video->h = video->video_stream->codec->height;
+ break;
+
+ default:
+ fprintf (stderr, "video_ready: unknown command from
decoding thread (%d)\n", commands [i]);
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+update_frame (VideoFfmpeg *video)
+{
+ AVFrame *frame = NULL;
+
+ //
+ // Experimental strategy: the strategy needs to be entirely redone
+ // for now I just pull the last frame and discard all the other
+ // intermediate frames that we already decoded.
+ //
+ //
+ g_mutex_lock (video->video_mutex);
+ while (g_async_queue_length (video->video_frames) != 0){
+ void *rgb;
+
+ if (frame != NULL)
+ av_free (frame);
+
+ frame = (AVFrame *) g_async_queue_pop (video->video_frames);
+ }
+ g_mutex_unlock (video->video_mutex);
+
+ if (frame != NULL){
+ convert_to_rgb (video, frame);
+ av_free (frame);
+ }
+}
+
+void
+VideoFfmpeg::render (Surface *s, double *affine, int x, int y, int width, int
height)
+{
+ double actual [6];
+ double *use_affine = item_get_affine (affine, xform, actual);
+ cairo_pattern_t *pattern, *old_pattern;
+
+ update_frame (this);
+
+ cairo_save (s->cairo);
+ if (use_affine != NULL)
+ cairo_set_matrix (s->cairo, (cairo_matrix_t *) use_affine);
+
+ cairo_translate (s->cairo, this->x, this->y);
+ cairo_set_source_surface (s->cairo, video_cairo_surface, 0, 0);
+ cairo_paint (s->cairo);
+
+ cairo_rectangle (s->cairo, x, y, w, h);
+
+ cairo_restore (s->cairo);
+}
+
+static int ffmpeg_inited;
+
+static void
+ffmpeg_init ()
+{
+ if (!ffmpeg_inited){
+ video_lock = g_mutex_new ();
+ avcodec_init ();
+ av_register_all ();
+ avcodec_register_all ();
+ ffmpeg_inited = TRUE;
+ }
+}
+Video *
+video_ffmpeg_new (const char *filename, double x, double y)
+{
+ VideoFfmpeg *video;
+
+ ffmpeg_init ();
+
+ video = new VideoFfmpeg (filename, x, y);
+
+ return (Video *) video;
+}
+
+VideoFfmpeg::VideoFfmpeg (const char *filename, double x, double y) : Video
(filename, x, y)
+{
+ video_codec = NULL;
+ video_stream = NULL;
+ video_rgb_buffer = NULL;
+ video_cairo_surface = NULL;
+ video_scale_context = NULL;
+ av_format_context = NULL;
+
+ pipe (pipes);
+ fcntl (pipes [0], F_SETFL, O_NONBLOCK);
+
+ video_mutex = g_mutex_new ();
+ video_frames = g_async_queue_new ();
+ pipe_channel = g_io_channel_unix_new (pipes [0]);
+ g_io_add_watch (pipe_channel, G_IO_IN, video_ready, this);
+
+ decode_thread_id = g_thread_create (decoder, this, TRUE, NULL);
+}
+
+VideoFfmpeg::~VideoFfmpeg ()
+{
+ // TODO:
+ // * Ask our thread to shutdown nicely.
+ //
+ fprintf (stderr, "We should stop the thread");
+
+ g_async_queue_unref (video_frames);
+ g_mutex_free (video_mutex);
+ close (pipes [0]);
+ close (pipes [1]);
+ g_io_channel_close (pipe_channel);
+ g_free (video_rgb_buffer);
+ cairo_surface_destroy (video_cairo_surface);
+}
+
+
+/*
+ * video_new:
+ * @filename:
+ * @x, @y: Position where to position the video
+ *
+ */
+Video *
+video_new (const char *filename, double x, double y)
+{
+ return video_ffmpeg_new (filename, x, y);
+}
+
_______________________________________________
Mono-patches maillist - [email protected]
http://lists.ximian.com/mailman/listinfo/mono-patches