]> www.fi.muni.cz Git - evince.git/blob - backend/impress/impress-document.c
Use cairo image surfaces instead of GDK pixbufs for drawing pages and
[evince.git] / backend / impress / impress-document.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
2 /*
3  * Copyright (C) 2005, Jonathan Blandford <jrb@gnome.org>
4  * Copyright (C) 2005, Bastien Nocera <hadess@hadess.net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #include <gtk/gtk.h>
22 #include <glib/gi18n.h>
23 #include <string.h>
24
25 #include "imposter.h"
26 #include "impress-document.h"
27 #include "ev-document-thumbnails.h"
28 #include "ev-document-misc.h"
29
30 struct _ImpressDocumentClass
31 {
32   GObjectClass parent_class;
33 };
34
35 struct _ImpressDocument
36 {
37   GObject parent_instance;
38
39   ImpDoc *imp;
40   ImpRenderCtx *ctx;
41
42   GMutex *mutex;
43   GdkPixmap *pixmap;
44   GdkGC *gc;
45   PangoContext *pango_ctx;
46
47   /* Only used while rendering inside the mainloop */
48   int pagenum;
49   GdkPixbuf *pixbuf;
50   GCond *cond;
51 };
52
53 #define PAGE_WIDTH 1024
54 #define PAGE_HEIGHT 768
55
56 typedef struct _ImpressDocumentClass ImpressDocumentClass;
57
58 static void impress_document_document_iface_init (EvDocumentIface *iface);
59 static void impress_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface);
60
61 G_DEFINE_TYPE_WITH_CODE (ImpressDocument, impress_document, G_TYPE_OBJECT,
62                          { G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT,
63                                                   impress_document_document_iface_init);
64                            G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS,
65                                                   impress_document_document_thumbnails_iface_init);
66                          });
67
68 /* Renderer */
69 static void
70 imp_render_draw_bezier_real (GdkDrawable *d, GdkGC *gc, int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3)
71 {
72   int x, y, nx, ny;
73   int ax, bx, cx, ay, by, cy;
74   double t, t2, t3;
75
76   x = x0;
77   y = y0;
78
79   cx = 3 * (x1 - x0);
80   bx = 3 * (x2 - x1) - cx;
81   ax = x3 - x0 - cx - bx;
82   cy = 3 * (y1 - y0);
83   by = 3 * (y2 - y1) - cy;
84   ay = y3 - y0 - cy - by;
85
86   for (t = 0; t < 1; t += 0.01) {
87     t2 = t * t;
88     t3 = t2 * t;
89     nx = ax * t3 + bx * t2 + cx * t + x0;
90     ny = ay * t3 + by * t2 + cy * t + y0;
91     gdk_draw_line (d, gc, x, y, nx, ny);
92     x = nx;
93     y = ny;
94   }
95 }
96
97 static void
98 imp_render_get_size(void *drw_data, int *w, int *h)
99 {
100   ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
101
102   gdk_drawable_get_size(impress_document->pixmap, w, h);
103 }
104
105 static void
106 imp_render_set_fg_color(void *drw_data, ImpColor *color)
107 {
108   ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
109   GdkColor c;
110
111   c.red = color->red;
112   c.green = color->green;
113   c.blue = color->blue;
114   gdk_gc_set_rgb_fg_color(impress_document->gc, &c);
115 }
116
117 static void
118 imp_render_draw_line(void *drw_data, int x1, int y1, int x2, int y2)
119 {
120   ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
121
122   gdk_draw_line(impress_document->pixmap, impress_document->gc, x1, y1, x2, y2);
123 }
124
125 static void
126 imp_render_draw_rect(void *drw_data, int fill, int x, int y, int w, int h)
127 {
128   ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
129
130   gdk_draw_rectangle(impress_document->pixmap, impress_document->gc, fill, x, y, w, h);
131 }
132
133 static void
134 imp_render_draw_polygon(void *drw_data, int fill, ImpPoint *pts, int nr_pts)
135 {
136   ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
137
138   gdk_draw_polygon(impress_document->pixmap, impress_document->gc, fill, (GdkPoint *)pts, nr_pts);
139 }
140
141 static void
142 imp_render_draw_arc(void *drw_data, int fill, int x, int y, int w, int h, int sa, int ea)
143 {
144   ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
145
146   gdk_draw_arc(impress_document->pixmap, impress_document->gc, fill, x, y, w, h, sa * 64, ea * 64);
147 }
148
149 static void
150 imp_render_draw_bezier(void *drw_data, int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3)
151 {
152   ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
153
154   imp_render_draw_bezier_real (impress_document->pixmap, impress_document->gc, x0, y0, x1, y1, x2, y2, x3, y3);
155 }
156
157 static void *
158 imp_render_open_image(void *drw_data, const unsigned char *pix, size_t size)
159 {
160   GdkPixbufLoader *gpl;
161   GdkPixbuf *pb;
162
163   gpl = gdk_pixbuf_loader_new();
164   gdk_pixbuf_loader_write(gpl, pix, size, NULL);
165   gdk_pixbuf_loader_close(gpl, NULL);
166   pb = gdk_pixbuf_loader_get_pixbuf(gpl);
167   return pb;
168 }
169
170 static void
171 imp_render_get_image_size(void *drw_data, void *img_data, int *w, int *h)
172 {
173   GdkPixbuf *pb = (GdkPixbuf *) img_data;
174
175   *w = gdk_pixbuf_get_width(pb);
176   *h = gdk_pixbuf_get_height(pb);
177 }
178
179 static void *
180 imp_render_scale_image(void *drw_data, void *img_data, int w, int h)
181 {
182   GdkPixbuf *pb = (GdkPixbuf *) img_data;
183
184   return gdk_pixbuf_scale_simple(pb, w, h, GDK_INTERP_BILINEAR);
185 }
186
187 static void
188 imp_render_draw_image(void *drw_data, void *img_data, int x, int y, int w, int h)
189 {
190   ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
191   GdkPixbuf *pb = (GdkPixbuf *) img_data;
192
193   gdk_draw_pixbuf(impress_document->pixmap, impress_document->gc, pb, 0, 0, x, y, w, h, GDK_RGB_DITHER_NONE, 0, 0);
194 }
195
196 static void
197 imp_render_close_image(void *drw_data, void *img_data)
198 {
199   GdkPixbuf *pb = (GdkPixbuf *) img_data;
200
201   g_object_unref(G_OBJECT(pb));
202 }
203
204 static char *
205 imp_render_markup(const char *text, size_t len, int styles, int size)
206 {
207   double scr_mm, scr_px, dpi;
208   char *esc;
209   char *ret;
210   int sz;
211
212   scr_mm = gdk_screen_get_height_mm(gdk_screen_get_default());
213   scr_px = gdk_screen_get_height(gdk_screen_get_default());
214   dpi = (scr_px / scr_mm) * 25.4; 
215   sz = (int) ((double) size * 72.0 * PANGO_SCALE / dpi);
216   esc = g_markup_escape_text(text, len);
217   ret = g_strdup_printf("<span size ='%d'>%s</span>", sz, esc);
218   g_free(esc);
219   return ret;
220 }
221
222 static void
223 imp_render_get_text_size(void *drw_data, const char *text, size_t len, int size, int styles, int *w, int *h)
224 {
225   ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
226   PangoLayout *lay;
227   int pw, ph;
228   char *m;
229
230   g_return_if_fail (impress_document->pango_ctx != NULL);
231
232   lay = pango_layout_new(impress_document->pango_ctx);
233   m = imp_render_markup(text, len, styles, size);
234   pango_layout_set_markup(lay, m, strlen(m));
235   pango_layout_get_size(lay, &pw, &ph);
236   g_object_unref(lay);
237   g_free(m);
238   *w = pw / PANGO_SCALE;
239   *h = ph / PANGO_SCALE;
240 }
241
242 static void
243 imp_render_draw_text(void *drw_data, int x, int y, const char *text, size_t len, int size, int styles)
244 {
245   ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
246   PangoLayout *lay;
247   char *m;
248
249   g_return_if_fail (impress_document->pango_ctx != NULL);
250
251   lay = pango_layout_new(impress_document->pango_ctx);
252   m = imp_render_markup(text, len, styles, size);
253   pango_layout_set_markup(lay, m, strlen(m));
254   gdk_draw_layout(impress_document->pixmap, impress_document->gc, x, y, lay);
255   g_object_unref(lay);
256   g_free(m);
257 }
258
259 static const ImpDrawer imp_render_functions = {
260   imp_render_get_size,
261   imp_render_set_fg_color,
262   imp_render_draw_line,
263   imp_render_draw_rect,
264   imp_render_draw_polygon,
265   imp_render_draw_arc,
266   imp_render_draw_bezier,
267   imp_render_open_image,
268   imp_render_get_image_size,
269   imp_render_scale_image,
270   imp_render_draw_image,
271   imp_render_close_image,
272   imp_render_get_text_size,
273   imp_render_draw_text
274 };
275
276 /* Document interface */
277 static gboolean
278 impress_document_load (EvDocument  *document,
279                     const char  *uri,
280                     GError     **error)
281 {
282   ImpressDocument *impress_document = IMPRESS_DOCUMENT (document);
283   gchar *filename;
284   ImpDoc *imp;
285   int err;
286
287   /* FIXME: Could we actually load uris ? */
288   filename = g_filename_from_uri (uri, NULL, error);
289   if (!filename)
290     {
291       g_set_error (error,
292                    EV_DOCUMENT_ERROR,
293                    EV_DOCUMENT_ERROR_INVALID,
294                    _("Remote files aren't supported"));
295       return FALSE;
296     }
297
298   imp = imp_open (filename, &err);
299
300   if (!imp)
301     {
302       g_set_error (error,
303                    EV_DOCUMENT_ERROR,
304                    EV_DOCUMENT_ERROR_INVALID,
305                    _("Invalid document"));
306       g_free (filename);
307       return FALSE;
308     }
309   impress_document->imp = imp;
310
311   return TRUE;
312 }
313
314 static gboolean
315 impress_document_save (EvDocument  *document,
316                       const char  *uri,
317                       GError     **error)
318 {
319         return FALSE;
320 }
321
322 static int
323 impress_document_get_n_pages (EvDocument  *document)
324 {
325   ImpressDocument *impress_document = IMPRESS_DOCUMENT (document);
326
327   g_return_val_if_fail (IMPRESS_IS_DOCUMENT (document), 0);
328   g_return_val_if_fail (impress_document->imp != NULL, 0);
329
330   return imp_nr_pages (impress_document->imp);
331 }
332
333 static void
334 impress_document_get_page_size (EvDocument   *document,
335                              int           page,
336                              double       *width,
337                              double       *height)
338 {
339   ImpressDocument *impress_document = IMPRESS_DOCUMENT (document);
340
341   g_return_if_fail (IMPRESS_IS_DOCUMENT (document));
342   g_return_if_fail (impress_document->imp != NULL);
343
344   //FIXME
345   *width = PAGE_WIDTH;
346   *height = PAGE_HEIGHT;
347 }
348
349 static gboolean
350 imp_render_get_from_drawable (ImpressDocument *impress_document)
351 {
352   ImpPage *page;
353
354   page = imp_get_page (impress_document->imp, impress_document->pagenum);
355
356   g_return_val_if_fail (page != NULL, FALSE);
357
358   ev_document_doc_mutex_lock ();
359   imp_context_set_page (impress_document->ctx, page);
360   imp_render (impress_document->ctx, impress_document);
361   ev_document_doc_mutex_unlock ();
362
363   impress_document->pixbuf = gdk_pixbuf_get_from_drawable (NULL,
364                                          GDK_DRAWABLE (impress_document->pixmap),
365                                          NULL,
366                                          0, 0,
367                                          0, 0,
368                                          PAGE_WIDTH, PAGE_HEIGHT);
369
370   g_cond_broadcast (impress_document->cond);
371   return FALSE;
372 }
373
374 static GdkPixbuf *
375 impress_document_render_pixbuf (EvDocument      *document,
376                                 EvRenderContext *rc)
377 {
378   ImpressDocument *impress_document = IMPRESS_DOCUMENT (document);
379   GdkPixbuf       *pixbuf;
380
381   g_return_val_if_fail (IMPRESS_IS_DOCUMENT (document), NULL);
382   g_return_val_if_fail (impress_document->imp != NULL, NULL);
383   
384   impress_document->pagenum = rc->page;
385
386   g_mutex_lock (impress_document->mutex);
387   impress_document->cond = g_cond_new ();
388
389   ev_document_fc_mutex_unlock ();
390   ev_document_doc_mutex_unlock ();
391   g_idle_add ((GSourceFunc) imp_render_get_from_drawable, impress_document);
392   g_cond_wait (impress_document->cond, impress_document->mutex);
393   g_cond_free (impress_document->cond);
394   ev_document_doc_mutex_lock ();
395   ev_document_fc_mutex_lock ();
396   
397   g_mutex_unlock (impress_document->mutex);
398
399   pixbuf = impress_document->pixbuf;
400   impress_document->pixbuf = NULL;
401
402   return pixbuf;
403 }
404
405 static cairo_surface_t *
406 impress_document_render (EvDocument      *document,
407                          EvRenderContext *rc)
408 {
409   GdkPixbuf *pixbuf;
410   cairo_surface_t *surface, *scaled_surface;
411
412   pixbuf = impress_document_render_pixbuf (document, rc);
413   
414   /* FIXME: impress backend should be ported to cairo */
415   surface = ev_document_misc_surface_from_pixbuf (pixbuf);
416   g_object_unref (pixbuf);
417
418   scaled_surface = ev_document_misc_surface_rotate_and_scale (surface,
419                                                               (PAGE_WIDTH * rc->scale) + 0.5,
420                                                               (PAGE_HEIGHT * rc->scale) + 0.5,
421                                                               rc->rotation);
422   cairo_surface_destroy (surface);
423
424   return scaled_surface;
425 }
426
427 static void
428 impress_document_finalize (GObject *object)
429 {
430   ImpressDocument *impress_document = IMPRESS_DOCUMENT (object);
431
432   if (impress_document->mutex)
433     g_mutex_free (impress_document->mutex);
434
435   if (impress_document->imp)
436     imp_close (impress_document->imp);
437
438   if (impress_document->ctx)
439     imp_delete_context (impress_document->ctx);
440
441   if (impress_document->pango_ctx)
442     g_object_unref (impress_document->pango_ctx);
443
444   if (impress_document->pixmap)
445     g_object_unref (G_OBJECT (impress_document->pixmap));
446
447   if (impress_document->gc)
448     g_object_unref (impress_document->gc);
449
450   G_OBJECT_CLASS (impress_document_parent_class)->finalize (object);
451 }
452
453 static void
454 impress_document_class_init (ImpressDocumentClass *klass)
455 {
456   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
457
458   gobject_class->finalize = impress_document_finalize;
459 }
460
461 static gboolean
462 impress_document_can_get_text (EvDocument *document)
463 {
464   return FALSE;
465 }
466
467 static EvDocumentInfo *
468 impress_document_get_info (EvDocument *document)
469 {
470   EvDocumentInfo *info;
471
472   info = g_new0 (EvDocumentInfo, 1);
473   info->fields_mask = 0;
474
475   return info;
476 }
477
478 static void
479 impress_document_document_iface_init (EvDocumentIface *iface)
480 {
481   iface->load = impress_document_load;
482   iface->save = impress_document_save;
483   iface->can_get_text = impress_document_can_get_text;
484   iface->get_n_pages = impress_document_get_n_pages;
485   iface->get_page_size = impress_document_get_page_size;
486   iface->render = impress_document_render;
487   iface->get_info = impress_document_get_info;
488 }
489
490 static GdkPixbuf *
491 impress_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document,
492                                            EvRenderContext      *rc, 
493                                            gboolean              border)
494 {
495   GdkPixbuf *pixbuf;
496   GdkPixbuf *scaled_pixbuf;
497   gdouble w, h;
498
499   pixbuf = impress_document_render_pixbuf (EV_DOCUMENT (document), rc);
500   scaled_pixbuf = gdk_pixbuf_scale_simple (pixbuf,
501                                            (PAGE_WIDTH * rc->scale),
502                                            (PAGE_HEIGHT * rc->scale),
503                                            GDK_INTERP_BILINEAR);
504   g_object_unref (pixbuf);
505
506   if (border)
507     {
508       GdkPixbuf *tmp_pixbuf = scaled_pixbuf;
509       
510       scaled_pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, tmp_pixbuf);
511       g_object_unref (tmp_pixbuf);
512     }
513
514   return scaled_pixbuf;
515 }
516
517 static void
518 impress_document_thumbnails_get_dimensions (EvDocumentThumbnails *document,
519                                             EvRenderContext      *rc,
520                                             gint                 *width,
521                                             gint                 *height)
522 {
523   gdouble page_width, page_height;
524
525   impress_document_get_page_size (EV_DOCUMENT (document),
526                                   rc->page,
527                                   &page_width, &page_height);
528   
529   if (rc->rotation == 90 || rc->rotation == 270)
530     {
531       *width = (gint) (page_height * rc->scale);
532       *height = (gint) (page_width * rc->scale);
533     }
534   else
535     {
536       *width = (gint) (page_width * rc->scale);
537       *height = (gint) (page_height * rc->scale);
538     }
539 }
540
541 static void
542 impress_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface)
543 {
544   iface->get_thumbnail = impress_document_thumbnails_get_thumbnail;
545   iface->get_dimensions = impress_document_thumbnails_get_dimensions;
546 }
547
548 static void
549 impress_document_init (ImpressDocument *impress_document)
550 {
551   GdkWindow *window;
552
553   impress_document->mutex = g_mutex_new ();
554   impress_document->ctx = imp_create_context(&imp_render_functions);
555
556   window = gdk_screen_get_root_window (gdk_screen_get_default ());
557
558   impress_document->pixmap = gdk_pixmap_new (window,
559                                              PAGE_WIDTH, PAGE_HEIGHT, -1);
560   impress_document->gc = gdk_gc_new (impress_document->pixmap);
561   impress_document->pango_ctx = gdk_pango_context_get_for_screen (gdk_screen_get_default ());
562 }
563
564 /*
565  * vim: sw=2 ts=8 cindent noai bs=2
566  */