]> www.fi.muni.cz Git - evince.git/blob - backend/dvi/cairo-device.c
8d6553d6cd6ea89b98cd0f71a2e2ecf8855d27f4
[evince.git] / backend / dvi / cairo-device.c
1 /*
2  * Copyright (C) 2007 Carlos Garcia Campos <carlosgc@gnome.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2, or (at your option)
7  * any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18
19 #include <config.h>
20
21 #include <stdlib.h>
22 #include <gdk/gdk.h>
23 #ifdef HAVE_SPECTRE
24 #include <libspectre/spectre.h>
25 #endif
26
27 #include "cairo-device.h"
28
29 typedef struct {
30         cairo_t *cr;
31
32         gint xmargin;
33         gint ymargin;
34
35         gdouble scale;
36         
37         Ulong fg;
38         Ulong bg;
39
40 } DviCairoDevice;
41
42 static void
43 dvi_cairo_draw_glyph (DviContext  *dvi,
44                       DviFontChar *ch,
45                       int          x0,
46                       int          y0)
47 {
48         DviCairoDevice  *cairo_device;
49         int              x, y, w, h;
50         gboolean         isbox;
51         DviGlyph        *glyph;
52         cairo_surface_t *surface;
53
54         cairo_device = (DviCairoDevice *) dvi->device.device_data;
55
56         glyph = &ch->grey;
57
58         isbox = (glyph->data == NULL ||
59                  (dvi->params.flags & MDVI_PARAM_CHARBOXES) ||
60                  MDVI_GLYPH_ISEMPTY (glyph->data));
61
62         x = - glyph->x + x0 + cairo_device->xmargin;
63         y = - glyph->y + y0 + cairo_device->ymargin;
64         w = glyph->w;
65         h = glyph->h;
66
67         surface = cairo_get_target (cairo_device->cr);
68         if (x < 0 || y < 0
69             || x + w > cairo_image_surface_get_width (surface)
70             || y + h > cairo_image_surface_get_height (surface))
71                 return;
72
73         cairo_save (cairo_device->cr);
74         if (isbox) {
75                 cairo_rectangle (cairo_device->cr,
76                                  x - cairo_device->xmargin,
77                                  y - cairo_device->ymargin,
78                                  w, h);
79                 cairo_stroke (cairo_device->cr);
80         } else {
81                 cairo_translate (cairo_device->cr, x, y);
82                 cairo_set_source_surface (cairo_device->cr,
83                                           (cairo_surface_t *) glyph->data,
84                                           0, 0);
85                 cairo_paint (cairo_device->cr);
86         }
87
88         cairo_restore (cairo_device->cr);
89 }
90
91 static void
92 dvi_cairo_draw_rule (DviContext *dvi,
93                      int         x,
94                      int         y,
95                      Uint        width,
96                      Uint        height,
97                      int         fill)
98 {
99         DviCairoDevice *cairo_device;
100         Ulong           color;
101
102         cairo_device = (DviCairoDevice *) dvi->device.device_data;
103
104         color = cairo_device->fg;
105         
106         cairo_save (cairo_device->cr);
107
108         cairo_set_line_width (cairo_device->cr,
109                               cairo_get_line_width (cairo_device->cr) * cairo_device->scale);
110         cairo_set_source_rgb (cairo_device->cr,
111                               ((color >> 16) & 0xff) / 255.,
112                               ((color >> 8) & 0xff) / 255.,
113                               ((color >> 0) & 0xff) / 255.);
114
115         cairo_rectangle (cairo_device->cr,
116                          x + cairo_device->xmargin,
117                          y + cairo_device->ymargin,
118                          width, height);
119         if (fill == 0) {
120                 cairo_stroke (cairo_device->cr);
121         } else {
122                 cairo_fill (cairo_device->cr);
123         }
124
125         cairo_restore (cairo_device->cr);
126 }
127
128 #ifdef HAVE_SPECTRE
129 static void
130 dvi_cairo_draw_ps (DviContext *dvi,
131                    const char *filename,
132                    int         x,
133                    int         y,
134                    Uint        width,
135                    Uint        height)
136 {
137         DviCairoDevice       *cairo_device;
138         unsigned char        *data = NULL;
139         int                   row_length;
140         SpectreDocument      *psdoc;
141         SpectreRenderContext *rc;
142         int                   w, h;
143         SpectreStatus         status;
144         cairo_surface_t      *image;
145
146         cairo_device = (DviCairoDevice *) dvi->device.device_data;
147
148         psdoc = spectre_document_new ();
149         spectre_document_load (psdoc, filename);
150         if (spectre_document_status (psdoc)) {
151                 spectre_document_free (psdoc);
152                 return;
153         }
154
155         spectre_document_get_page_size (psdoc, &w, &h);
156
157         rc = spectre_render_context_new ();
158         spectre_render_context_set_scale (rc,
159                                           (double)width / w,
160                                           (double)height / h);
161         spectre_document_render_full (psdoc, rc, &data, &row_length);   
162         status = spectre_document_status (psdoc);
163
164         spectre_render_context_free (rc);
165         spectre_document_free (psdoc);
166
167         if (status) {
168                 g_warning ("Error rendering PS document %s: %s\n",
169                            filename, spectre_status_to_string (status));
170                 free (data);
171                 
172                 return;
173         }
174
175         image = cairo_image_surface_create_for_data ((unsigned char *)data,
176                                                      CAIRO_FORMAT_RGB24,
177                                                      width, height,
178                                                      row_length);
179
180         cairo_save (cairo_device->cr);
181
182         cairo_translate (cairo_device->cr,
183                          x + cairo_device->xmargin,
184                          y + cairo_device->ymargin);
185         cairo_set_source_surface (cairo_device->cr, image, 0, 0); 
186         cairo_paint (cairo_device->cr);
187
188         cairo_restore (cairo_device->cr);
189
190         cairo_surface_destroy (image);
191         free (data);
192 }
193 #endif /* HAVE_SPECTRE */
194
195 static int
196 dvi_cairo_alloc_colors (void  *device_data,
197                         Ulong *pixels,
198                         int    npixels,
199                         Ulong  fg,
200                         Ulong  bg,
201                         double gamma,
202                         int    density)
203 {
204         double  frac;
205         GdkColor color, color_fg;
206         int     i, n;
207         unsigned int alpha;
208
209         color_fg.red = (fg >> 16) & 0xff;
210         color_fg.green = (fg >> 8) & 0xff;
211         color_fg.blue = (fg >> 0) & 0xff;
212
213         n = npixels - 1;
214         for (i = 0; i < npixels; i++) {
215                 frac = (gamma > 0) ?
216                         pow ((double)i / n, 1 / gamma) :
217                         1 - pow ((double)(n - i) / n, -gamma);
218                 
219                 color.red = frac * color_fg.red;
220                 color.green = frac * color_fg.green;
221                 color.blue = frac * color_fg.blue;
222                 alpha = frac * 0xFF;
223
224                 pixels[i] = (alpha << 24) + (color.red << 16) + (color.green << 8) + color.blue;
225         }
226
227         return npixels;
228 }
229
230 static void *
231 dvi_cairo_create_image (void *device_data,
232                         Uint  width,
233                         Uint  height,
234                         Uint  bpp)
235 {
236         return cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
237 }
238
239 static void
240 dvi_cairo_free_image (void *ptr)
241 {
242         cairo_surface_destroy ((cairo_surface_t *)ptr);
243 }
244
245 static void
246 dvi_cairo_put_pixel (void *image, int x, int y, Ulong color)
247 {
248         cairo_surface_t *surface;
249         gint             rowstride;
250         guint32         *p;
251
252         surface = (cairo_surface_t *) image;
253
254         rowstride = cairo_image_surface_get_stride (surface);
255         p = (guint32*) (cairo_image_surface_get_data (surface) + y * rowstride + x * 4);
256
257         /* per cairo docs, must flush before modifying outside of cairo */
258         cairo_surface_flush(surface);
259         *p = color;
260 }
261
262 static void
263 dvi_cairo_image_done (void *ptr)
264 {
265         cairo_surface_mark_dirty((cairo_surface_t *)ptr);
266 }
267
268 static void
269 dvi_cairo_set_color (void *device_data, Ulong fg, Ulong bg)
270 {
271         DviCairoDevice *cairo_device = (DviCairoDevice *) device_data;
272
273         cairo_device->fg = fg;
274         cairo_device->bg = bg;
275 }
276
277 /* Public methods */
278 void
279 mdvi_cairo_device_init (DviDevice *device)
280 {
281         device->device_data = g_new0 (DviCairoDevice, 1);
282
283         device->draw_glyph = dvi_cairo_draw_glyph;
284         device->draw_rule = dvi_cairo_draw_rule;
285         device->alloc_colors = dvi_cairo_alloc_colors;
286         device->create_image = dvi_cairo_create_image;
287         device->free_image = dvi_cairo_free_image;
288         device->put_pixel = dvi_cairo_put_pixel;
289         device->image_done = dvi_cairo_image_done;
290         device->set_color = dvi_cairo_set_color;
291 #ifdef HAVE_SPECTRE
292         device->draw_ps = dvi_cairo_draw_ps;
293 #else
294         device->draw_ps = NULL;
295 #endif
296         device->refresh = NULL;
297 }
298
299 void
300 mdvi_cairo_device_free (DviDevice *device)
301 {
302         DviCairoDevice *cairo_device;
303
304         cairo_device = (DviCairoDevice *) device->device_data;
305
306         if (cairo_device->cr)
307                 cairo_destroy (cairo_device->cr);
308
309         g_free (cairo_device);
310 }
311
312 cairo_surface_t *
313 mdvi_cairo_device_get_surface (DviDevice *device)
314 {
315         DviCairoDevice *cairo_device;
316
317         cairo_device = (DviCairoDevice *) device->device_data;
318
319         return cairo_surface_reference (cairo_get_target (cairo_device->cr));
320 }
321
322 void
323 mdvi_cairo_device_render (DviContext* dvi)
324 {
325         DviCairoDevice  *cairo_device;
326         gint             page_width;
327         gint             page_height;
328         cairo_surface_t *surface;
329         guchar          *pixels;
330         gint             rowstride;
331         static const cairo_user_data_key_t key;
332
333         cairo_device = (DviCairoDevice *) dvi->device.device_data;
334
335         if (cairo_device->cr)
336                 cairo_destroy (cairo_device->cr);
337
338         page_width = dvi->dvi_page_w * dvi->params.conv + 2 * cairo_device->xmargin;
339         page_height = dvi->dvi_page_h * dvi->params.vconv + 2 * cairo_device->ymargin;
340
341         rowstride = page_width * 4;
342         pixels = (guchar *) g_malloc (page_height * rowstride);
343         memset (pixels, 0xff, page_height * rowstride);
344
345         surface = cairo_image_surface_create_for_data (pixels,
346                                                        CAIRO_FORMAT_ARGB32,
347                                                        page_width, page_height,
348                                                        rowstride);
349         cairo_surface_set_user_data (surface, &key,
350                                      pixels, (cairo_destroy_func_t)g_free);
351
352         cairo_device->cr = cairo_create (surface);
353         cairo_surface_destroy (surface);
354
355         mdvi_dopage (dvi, dvi->currpage);
356 }
357
358 void
359 mdvi_cairo_device_set_margins (DviDevice *device,
360                                gint       xmargin,
361                                gint       ymargin)
362 {
363         DviCairoDevice *cairo_device;
364
365         cairo_device = (DviCairoDevice *) device->device_data;
366
367         cairo_device->xmargin = xmargin;
368         cairo_device->ymargin = ymargin;
369 }
370
371 void
372 mdvi_cairo_device_set_scale (DviDevice *device,
373                              gdouble    scale)
374 {
375         DviCairoDevice *cairo_device;
376
377         cairo_device = (DviCairoDevice *) device->device_data;
378
379         cairo_device->scale = scale;
380 }