+2005-06-07 Marco Pesenti Gritti <mpg@redhat.com>
+
+ * backend/Makefile.am:
+ * backend/ev-async-renderer.c: (ev_async_renderer_get_type),
+ (ev_async_renderer_class_init), (ev_async_renderer_render_pixbuf):
+ * backend/ev-async-renderer.h:
+
+ Add an async renderer interface (method + callback) which
+ is useful for backends like ps.
+
+ * backend/ev-job-queue.c: (remove_job_from_async_queue),
+ (add_job_to_async_queue), (job_finished_cb), (handle_job),
+ (ev_job_queue_run_next), (ev_job_queue_init), (find_queue),
+ (ev_job_queue_add_job), (move_job_async), (move_job),
+ (ev_job_queue_update_job), (ev_job_queue_remove_job):
+
+ Add queues for async renderer, these are executed on the
+ main thread.
+
+ * backend/ev-jobs.c: (ev_job_render_new), (render_finished_cb),
+ (ev_job_render_run):
+ * backend/ev-jobs.h:
+
+ If the backend support async renderer interface use it.
+
+ * ps/ps-document.c: (ps_document_init), (push_pixbuf),
+ (setup_pixmap), (ps_document_get_type),
+ (ps_async_renderer_render_pixbuf),
+ (ps_document_document_iface_init), (ps_async_renderer_iface_init):
+
+ Implement async renderer interface.
+
2005-06-07 Nickolay V. Shmyrev <<nshmyrev@yandex.ru>>
* shell/ev-sidebar-links.c: (ev_sidebar_links_dispose):
noinst_LTLIBRARIES = libevbackend.la
libevbackend_la_SOURCES= \
+ ev-async-renderer.c \
+ ev-async-renderer.h \
ev-backend-marshal.c \
ev-link.c \
ev-link.h \
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/*
+ * Copyright (C) 2004 Marco Pesenti Gritti
+ *
+ * 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include "ev-async-renderer.h"
+
+static void ev_async_renderer_class_init (gpointer g_class);
+
+enum
+{
+ RENDER_FINISHED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+GType
+ev_async_renderer_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0))
+ {
+ static const GTypeInfo our_info =
+ {
+ sizeof (EvAsyncRendererIface),
+ NULL,
+ NULL,
+ (GClassInitFunc)ev_async_renderer_class_init
+ };
+
+ type = g_type_register_static (G_TYPE_INTERFACE,
+ "EvAsyncRenderer",
+ &our_info, (GTypeFlags)0);
+ }
+
+ return type;
+}
+
+static void
+ev_async_renderer_class_init (gpointer g_class)
+{
+ signals[RENDER_FINISHED] =
+ g_signal_new ("render_finished",
+ EV_TYPE_ASYNC_RENDERER,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EvAsyncRendererIface, render_finished),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1,
+ GDK_TYPE_PIXBUF);
+}
+
+void
+ev_async_renderer_render_pixbuf (EvAsyncRenderer *async_renderer,
+ int page,
+ double scale)
+{
+ EvAsyncRendererIface *iface = EV_ASYNC_RENDERER_GET_IFACE (async_renderer);
+
+ iface->render_pixbuf (async_renderer, page, scale);
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/*
+ * Copyright (C) 2000-2003 Marco Pesenti Gritti
+ *
+ * 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef EV_ASYNC_RENDERER_H
+#define EV_ASYNC_RENDERER_H
+
+#include <glib-object.h>
+#include <glib.h>
+#include <gdk/gdkpixbuf.h>
+
+G_BEGIN_DECLS
+
+#define EV_TYPE_ASYNC_RENDERER (ev_async_renderer_get_type ())
+#define EV_ASYNC_RENDERER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EV_TYPE_ASYNC_RENDERER, EvAsyncRenderer))
+#define EV_ASYNC_RENDERER_IFACE(k) (G_TYPE_CHECK_CLASS_CAST((k), EV_TYPE_ASYNC_RENDERER, EvAsyncRendererIface))
+#define EV_IS_ASYNC_RENDERER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EV_TYPE_ASYNC_RENDERER))
+#define EV_IS_ASYNC_RENDERER_IFACE(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EV_TYPE_ASYNC_RENDERER))
+#define EV_ASYNC_RENDERER_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), EV_TYPE_ASYNC_RENDERER, EvAsyncRendererIface))
+
+typedef struct _EvAsyncRenderer EvAsyncRenderer;
+typedef struct _EvAsyncRendererIface EvAsyncRendererIface;
+
+struct _EvAsyncRendererIface
+{
+ GTypeInterface base_iface;
+
+ void (* render_finished) (EvAsyncRenderer *renderer,
+ GdkPixbuf *pixbuf);
+
+ void (* render_pixbuf) (EvAsyncRenderer *renderer,
+ int page,
+ double scale);
+};
+
+GType ev_async_renderer_get_type (void);
+void ev_async_renderer_render_pixbuf (EvAsyncRenderer *renderer,
+ int page,
+ double scale);
+
+G_END_DECLS
+
+#endif
static GQueue *thumbnail_queue_high = NULL;
static GQueue *thumbnail_queue_low = NULL;
+/* Queues used for backends supporting EvAsyncRender interface,
+ they are executed on the main thread */
+static GQueue *async_render_queue_high = NULL;
+static GQueue *async_render_queue_low = NULL;
+static gboolean async_rendering = FALSE;
+
+static void ev_job_queue_run_next (void);
+
static gboolean
remove_job_from_queue_locked (GQueue *queue, EvJob *job)
{
return FALSE;
}
+static gboolean
+remove_job_from_async_queue (GQueue *queue, EvJob *job)
+{
+ return remove_job_from_queue_locked (queue, job);
+}
+
+static void
+add_job_to_async_queue (GQueue *queue, EvJob *job)
+{
+ g_object_ref (job);
+ g_queue_push_tail (queue, job);
+}
+
static void
add_job_to_queue_locked (GQueue *queue,
EvJob *job)
g_cond_broadcast (render_cond);
}
-
static gboolean
notify_finished (GObject *job)
{
return FALSE;
}
+static void
+job_finished_cb (EvJob *job)
+{
+ g_object_unref (job);
+ async_rendering = FALSE;
+ ev_job_queue_run_next ();
+}
static void
handle_job (EvJob *job)
{
g_object_ref (G_OBJECT (job));
+ if (EV_JOB (job)->async) {
+ async_rendering = TRUE;
+ if (EV_IS_JOB_RENDER (job)) {
+ g_signal_connect (job, "finished",
+ G_CALLBACK (job_finished_cb), NULL);
+ } else {
+ g_assert_not_reached ();
+ }
+ }
+
if (EV_IS_JOB_THUMBNAIL (job))
ev_job_thumbnail_run (EV_JOB_THUMBNAIL (job));
else if (EV_IS_JOB_LINKS (job))
else if (EV_IS_JOB_RENDER (job))
ev_job_render_run (EV_JOB_RENDER (job));
- /* We let the idle own a ref, as we (the queue) are done with the job. */
- g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
- (GSourceFunc) notify_finished,
- job,
- g_object_unref);
+ if (!EV_JOB (job)->async) {
+ /* We let the idle own a ref, as we (the queue) are done with the job. */
+ g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
+ (GSourceFunc) notify_finished,
+ job,
+ g_object_unref);
+ }
}
static EvJob *
}
+static void
+ev_job_queue_run_next (void)
+{
+ EvJob *job;
+
+ job = (EvJob *) g_queue_pop_head (async_render_queue_high);
+
+ if (job == NULL) {
+ job = (EvJob *) g_queue_pop_head (async_render_queue_low);
+ }
+
+ /* Now that we have our job, we handle it */
+ if (job) {
+ handle_job (job);
+ g_object_unref (G_OBJECT (job));
+ }
+}
+
/* Public Functions */
void
ev_job_queue_init (void)
links_queue = g_queue_new ();
render_queue_high = g_queue_new ();
render_queue_low = g_queue_new ();
+ async_render_queue_high = g_queue_new ();
+ async_render_queue_low = g_queue_new ();
thumbnail_queue_high = g_queue_new ();
thumbnail_queue_low = g_queue_new ();
find_queue (EvJob *job,
EvJobPriority priority)
{
- if (EV_IS_JOB_RENDER (job)) {
- if (priority == EV_JOB_PRIORITY_HIGH)
- return render_queue_high;
- else
- return render_queue_low;
- } else if (EV_IS_JOB_THUMBNAIL (job)) {
- if (priority == EV_JOB_PRIORITY_HIGH)
- return thumbnail_queue_high;
- else
- return thumbnail_queue_low;
- } else if (EV_IS_JOB_LINKS (job)) {
- /* the priority doesn't effect links */
- return links_queue;
+ if (EV_JOB (job)->async) {
+ if (EV_IS_JOB_RENDER (job)) {
+ if (priority == EV_JOB_PRIORITY_HIGH)
+ return async_render_queue_high;
+ else
+ return async_render_queue_low;
+ }
+ } else {
+ if (EV_IS_JOB_RENDER (job)) {
+ if (priority == EV_JOB_PRIORITY_HIGH)
+ return render_queue_high;
+ else
+ return render_queue_low;
+ } else if (EV_IS_JOB_THUMBNAIL (job)) {
+ if (priority == EV_JOB_PRIORITY_HIGH)
+ return thumbnail_queue_high;
+ else
+ return thumbnail_queue_low;
+ } else if (EV_IS_JOB_LINKS (job)) {
+ /* the priority doesn't effect links */
+ return links_queue;
+ }
}
g_assert_not_reached ();
queue = find_queue (job, priority);
+ if (!EV_JOB (job)->async) {
+ g_mutex_lock (ev_queue_mutex);
+ add_job_to_queue_locked (queue, job);
+ g_mutex_unlock (ev_queue_mutex);
+ } else {
+ add_job_to_async_queue (queue, job);
+ if (!async_rendering) {
+ ev_job_queue_run_next ();
+ }
+ }
+}
+
+static gboolean
+move_job_async (EvJob *job, GQueue *old_queue, GQueue *new_queue)
+{
+ gboolean retval = FALSE;
+
+ g_object_ref (job);
+
+ if (remove_job_from_queue_locked (old_queue, job)) {
+ add_job_to_async_queue (new_queue, job);
+ retval = TRUE;
+ }
+
+ g_object_unref (job);
+
+ return retval;
+}
+
+static gboolean
+move_job (EvJob *job, GQueue *old_queue, GQueue *new_queue)
+{
+ gboolean retval = FALSE;
+
g_mutex_lock (ev_queue_mutex);
- add_job_to_queue_locked (queue, job);
+ g_object_ref (job);
+
+ if (remove_job_from_queue_locked (old_queue, job)) {
+ add_job_to_queue_locked (new_queue, job);
+ retval = TRUE;
+ }
+
+ g_object_unref (job);
g_mutex_unlock (ev_queue_mutex);
+
+ return retval;
}
gboolean
g_return_val_if_fail (EV_IS_JOB (job), FALSE);
- g_mutex_lock (ev_queue_mutex);
- g_object_ref (job);
-
- if (EV_IS_JOB_THUMBNAIL (job)) {
- if (new_priority == EV_JOB_PRIORITY_LOW) {
- if (remove_job_from_queue_locked (thumbnail_queue_high, job)) {
- add_job_to_queue_locked (thumbnail_queue_low, job);
- retval = TRUE;
- }
- } else if (new_priority == EV_JOB_PRIORITY_HIGH) {
- if (remove_job_from_queue_locked (thumbnail_queue_low, job)) {
- add_job_to_queue_locked (thumbnail_queue_high, job);
- retval = TRUE;
+ if (EV_JOB (job)->async) {
+ if (EV_IS_JOB_RENDER (job)) {
+ if (new_priority == EV_JOB_PRIORITY_LOW) {
+ retval = move_job_async (job, async_render_queue_high,
+ async_render_queue_low);
+ } else if (new_priority == EV_JOB_PRIORITY_HIGH) {
+ retval = move_job_async (job, async_render_queue_low,
+ async_render_queue_high);
}
+ } else {
+ g_assert_not_reached ();
}
- } else if (EV_IS_JOB_RENDER (job)) {
- if (new_priority == EV_JOB_PRIORITY_LOW) {
- if (remove_job_from_queue_locked (render_queue_high, job)) {
- add_job_to_queue_locked (render_queue_low, job);
- retval = TRUE;
+ } else {
+ if (EV_IS_JOB_THUMBNAIL (job)) {
+ if (new_priority == EV_JOB_PRIORITY_LOW) {
+ retval = move_job (job, thumbnail_queue_high,
+ thumbnail_queue_low);
+ } else if (new_priority == EV_JOB_PRIORITY_HIGH) {
+ retval = move_job (job, thumbnail_queue_low,
+ thumbnail_queue_high);
}
- } else if (new_priority == EV_JOB_PRIORITY_HIGH) {
- if (remove_job_from_queue_locked (render_queue_low, job)) {
- add_job_to_queue_locked (render_queue_high, job);
- retval = TRUE;
+ } else if (EV_IS_JOB_RENDER (job)) {
+ if (new_priority == EV_JOB_PRIORITY_LOW) {
+ retval = move_job (job, render_queue_high,
+ render_queue_low);
+ } else if (new_priority == EV_JOB_PRIORITY_HIGH) {
+ retval = move_job (job, render_queue_low,
+ render_queue_high);
}
+ } else {
+ g_assert_not_reached ();
}
- } else {
- /* We don't have a priority queue for any of the other jobs */
- }
- g_object_unref (job);
- g_mutex_unlock (ev_queue_mutex);
+ }
return retval;
}
g_return_val_if_fail (EV_IS_JOB (job), FALSE);
- g_mutex_lock (ev_queue_mutex);
-
- if (EV_IS_JOB_THUMBNAIL (job)) {
- retval = remove_job_from_queue_locked (thumbnail_queue_high, job);
- retval = retval || remove_job_from_queue_locked (thumbnail_queue_low, job);
- } else if (EV_IS_JOB_RENDER (job)) {
- retval = remove_job_from_queue_locked (render_queue_high, job);
- retval = retval || remove_job_from_queue_locked (render_queue_low, job);
- } else if (EV_IS_JOB_LINKS (job)) {
- retval = remove_job_from_queue_locked (links_queue, job);
+ if (EV_JOB (job)->async) {
+ if (EV_IS_JOB_RENDER (job)) {
+ retval = remove_job_from_async_queue (async_render_queue_high, job);
+ retval = retval || remove_job_from_async_queue (async_render_queue_low, job);
+ } else {
+ g_assert_not_reached ();
+ }
} else {
- g_assert_not_reached ();
+ g_mutex_lock (ev_queue_mutex);
+
+ if (EV_IS_JOB_THUMBNAIL (job)) {
+ retval = remove_job_from_queue_locked (thumbnail_queue_high, job);
+ retval = retval || remove_job_from_queue_locked (thumbnail_queue_low, job);
+ } else if (EV_IS_JOB_RENDER (job)) {
+ retval = remove_job_from_queue_locked (render_queue_high, job);
+ retval = retval || remove_job_from_queue_locked (render_queue_low, job);
+ } else if (EV_IS_JOB_LINKS (job)) {
+ retval = remove_job_from_queue_locked (links_queue, job);
+ } else {
+ g_assert_not_reached ();
+ }
+
+ g_mutex_unlock (ev_queue_mutex);
}
- g_mutex_unlock (ev_queue_mutex);
-
return retval;
}
#include "ev-job-queue.h"
#include "ev-document-thumbnails.h"
#include "ev-document-links.h"
+#include "ev-async-renderer.h"
static void ev_job_init (EvJob *job);
static void ev_job_class_init (EvJobClass *class);
job->target_height = height;
job->include_links = include_links;
+ if (EV_IS_ASYNC_RENDERER (document)) {
+ EV_JOB (job)->async = TRUE;
+ }
+
return EV_JOB (job);
}
+static void
+render_finished_cb (EvDocument *document, GdkPixbuf *pixbuf, EvJobRender *job)
+{
+ g_signal_handlers_disconnect_by_func (EV_JOB (job)->document,
+ render_finished_cb, job);
+
+ EV_JOB (job)->finished = TRUE;
+ job->pixbuf = g_object_ref (pixbuf);
+ ev_job_finished (EV_JOB (job));
+}
+
void
ev_job_render_run (EvJobRender *job)
{
ev_document_doc_mutex_lock ();
- job->pixbuf = ev_document_render_pixbuf (EV_JOB (job)->document,
- job->page,
- job->scale);
- if (job->include_links)
- job->link_mapping = ev_document_get_links (EV_JOB (job)->document, job->page);
- EV_JOB (job)->finished = TRUE;
+ if (EV_JOB (job)->async) {
+ EvAsyncRenderer *renderer = EV_ASYNC_RENDERER (EV_JOB (job)->document);
+ ev_async_renderer_render_pixbuf (renderer, job->page, job->scale);
+ g_signal_connect (EV_JOB (job)->document, "render_finished",
+ G_CALLBACK (render_finished_cb), job);
+ } else {
+ job->pixbuf = ev_document_render_pixbuf (EV_JOB (job)->document,
+ job->page,
+ job->scale);
+ if (job->include_links)
+ job->link_mapping = ev_document_get_links (EV_JOB (job)->document, job->page);
+
+ EV_JOB (job)->finished = TRUE;
+ }
+
ev_document_doc_mutex_unlock ();
}
GObject parent;
EvDocument *document;
gboolean finished;
+ gboolean async;
};
struct _EvJobClass
#include "ev-debug.h"
#include "gsdefaults.h"
#include "ev-ps-exporter.h"
+#include "ev-async-renderer.h"
#ifdef HAVE_LOCALE_H
# include <locale.h>
PS_DOCUMENT(gs)->gs_filename_unc : \
PS_DOCUMENT(gs)->gs_filename)
-GCond* pixbuf_cond = NULL;
-GMutex* pixbuf_mutex = NULL;
-GdkPixbuf *current_pixbuf = NULL;
-
/* structure to describe section of file to send to ghostscript */
struct record_list {
FILE *fp;
static gint start_interpreter(PSDocument * gs);
static void ps_document_document_iface_init (EvDocumentIface *iface);
static void ps_document_ps_exporter_iface_init (EvPSExporterIface *iface);
+static void ps_async_renderer_iface_init (EvAsyncRendererIface *iface);
static gboolean ps_document_widget_event (GtkWidget *widget, GdkEvent *event, gpointer data);
static GObjectClass *parent_class = NULL;
gs->ps_export_pagelist = NULL;
gs->ps_export_filename = NULL;
-
- pixbuf_cond = g_cond_new ();
- pixbuf_mutex = g_mutex_new ();
}
static void
pixbuf = gdk_pixbuf_get_from_drawable (NULL, gs->bpixmap, cmap,
0, 0, 0, 0,
width, height);
- g_mutex_lock (pixbuf_mutex);
- current_pixbuf = pixbuf;
- g_cond_signal (pixbuf_cond);
- g_mutex_unlock (pixbuf_mutex);
-
+ g_signal_emit_by_name (gs, "render_finished", pixbuf);
+ g_object_unref (pixbuf);
}
static void
int pixmap_width, pixmap_height;
ev_document_get_page_size (EV_DOCUMENT (gs), page, &width, &height);
- pixmap_width = floor (width * scale);
- pixmap_height = floor (height * scale);
+ pixmap_width = width * scale + 0.5;
+ pixmap_height = height * scale + 0.5;
if(gs->bpixmap) {
int w, h;
NULL
};
+ static const GInterfaceInfo async_renderer_info =
+ {
+ (GInterfaceInitFunc) ps_async_renderer_iface_init,
+ NULL,
+ NULL
+ };
+
gs_type = g_type_register_static(G_TYPE_OBJECT,
"PSDocument", &gs_info, 0);
g_type_add_interface_static (gs_type,
EV_TYPE_PS_EXPORTER,
&ps_exporter_info);
+ g_type_add_interface_static (gs_type,
+ EV_TYPE_ASYNC_RENDERER,
+ &async_renderer_info);
}
return gs_type;
return FALSE;
}
-static GdkPixbuf *
-ps_document_render_pixbuf (EvDocument *document, int page, double scale)
+static void
+ps_async_renderer_render_pixbuf (EvAsyncRenderer *renderer, int page, double scale)
{
- GdkPixbuf *pixbuf;
PSRenderJob job;
job.page = page;
job.scale = scale;
- job.document = PS_DOCUMENT (document);
- g_idle_add ((GSourceFunc)render_pixbuf_idle, &job);
-
- g_mutex_lock (pixbuf_mutex);
- while (!current_pixbuf)
- g_cond_wait (pixbuf_cond, pixbuf_mutex);
- pixbuf = current_pixbuf;
- current_pixbuf = NULL;
- g_mutex_unlock (pixbuf_mutex);
-
- LOG ("Pixbuf rendered %p\n", pixbuf);
-
- return pixbuf;
+ job.document = PS_DOCUMENT (renderer);
+ render_pixbuf_idle (&job);
}
static EvDocumentInfo *
iface->can_get_text = ps_document_can_get_text;
iface->get_n_pages = ps_document_get_n_pages;
iface->get_page_size = ps_document_get_page_size;
- iface->render_pixbuf = ps_document_render_pixbuf;
iface->get_info = ps_document_get_info;
}
+static void
+ps_async_renderer_iface_init (EvAsyncRendererIface *iface)
+{
+ iface->render_pixbuf = ps_async_renderer_render_pixbuf;
+}
+
static void
ps_document_ps_export_begin (EvPSExporter *exporter, const char *filename,
int first_page, int last_page)